summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp72
-rw-r--r--apct-tests/perftests/core/src/android/wm/RecentsAnimationPerfTest.java4
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java10
-rw-r--r--api/current.txt12
-rw-r--r--api/system-current.txt49
-rw-r--r--api/test-current.txt9
-rw-r--r--cmds/idmap2/Android.bp3
-rw-r--r--cmds/statsd/Android.bp149
-rw-r--r--cmds/statsd/src/FieldValue.cpp21
-rw-r--r--cmds/statsd/src/HashableDimensionKey.cpp10
-rw-r--r--cmds/statsd/src/HashableDimensionKey.h9
-rw-r--r--cmds/statsd/src/atoms.proto102
-rw-r--r--cmds/statsd/src/external/StatsPullerManager.cpp3
-rw-r--r--cmds/statsd/src/matchers/matcher_util.cpp5
-rw-r--r--cmds/statsd/src/metrics/MetricProducer.h197
-rw-r--r--cmds/statsd/src/state/StateListener.h52
-rw-r--r--cmds/statsd/src/state/StateManager.cpp92
-rw-r--r--cmds/statsd/src/state/StateManager.h77
-rw-r--r--cmds/statsd/src/state/StateTracker.cpp159
-rw-r--r--cmds/statsd/src/state/StateTracker.h95
-rw-r--r--cmds/statsd/tests/LogEntryMatcher_test.cpp1
-rw-r--r--cmds/statsd/tests/state/StateTracker_test.cpp356
-rw-r--r--config/dirty-image-objects2
-rw-r--r--config/hiddenapi-greylist.txt279
-rw-r--r--config/preloaded-classes2
-rw-r--r--config/preloaded-classes-blacklist1
-rw-r--r--core/java/android/accessibilityservice/AccessibilityGestureEvent.aidl (renamed from core/java/android/accessibilityservice/AccessibilityGestureInfo.aidl)2
-rw-r--r--core/java/android/accessibilityservice/AccessibilityGestureEvent.java (renamed from core/java/android/accessibilityservice/AccessibilityGestureInfo.java)24
-rw-r--r--core/java/android/accessibilityservice/AccessibilityService.java22
-rw-r--r--core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl4
-rw-r--r--core/java/android/app/ActivityThread.java18
-rw-r--r--core/java/android/app/ActivityTransitionState.java12
-rw-r--r--core/java/android/app/AppCompatCallbacks.java20
-rw-r--r--core/java/android/app/AppOpsManager.java27
-rw-r--r--core/java/android/app/EnterTransitionCoordinator.java16
-rw-r--r--core/java/android/app/Notification.java8
-rw-r--r--core/java/android/app/UiAutomation.java4
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java20
-rw-r--r--core/java/android/content/IntentFilter.java4
-rw-r--r--core/java/android/content/pm/LauncherApps.java6
-rw-r--r--core/java/android/content/pm/PackageManager.java4
-rw-r--r--core/java/android/content/pm/PackageManagerInternal.java28
-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/hardware/camera2/CameraDevice.java9
-rw-r--r--core/java/android/hardware/camera2/CaptureRequest.java3
-rw-r--r--core/java/android/hardware/camera2/impl/CameraDeviceImpl.java56
-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/usb/IUsbManager.aidl12
-rw-r--r--core/java/android/os/ParcelFileDescriptor.java2
-rw-r--r--core/java/android/os/Process.java6
-rw-r--r--core/java/android/os/SystemProperties.java2
-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/SearchIndexablesContract.java24
-rw-r--r--core/java/android/provider/SearchIndexablesProvider.java23
-rw-r--r--core/java/android/provider/Settings.java20
-rw-r--r--core/java/android/service/carrier/ApnService.java2
-rw-r--r--core/java/android/service/carrier/IApnSourceService.aidl (renamed from telephony/java/com/android/internal/telephony/IApnSourceService.aidl)3
-rw-r--r--core/java/android/service/notification/NotificationListenerService.java15
-rw-r--r--core/java/android/service/voice/AlwaysOnHotwordDetector.java1
-rw-r--r--core/java/android/text/TextLine.java4
-rw-r--r--core/java/android/view/IPinnedStackController.aidl18
-rw-r--r--core/java/android/view/IPinnedStackListener.aidl62
-rw-r--r--core/java/android/view/IRecentsAnimationRunner.aidl4
-rw-r--r--core/java/android/view/IRemoteAnimationRunner.aidl2
-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/NotificationHeaderView.java11
-rw-r--r--core/java/android/view/SurfaceControl.aidl1
-rw-r--r--core/java/android/view/SurfaceView.java25
-rw-r--r--core/java/android/view/SyncRtSurfaceTransactionApplier.java148
-rw-r--r--core/java/android/view/View.java4
-rw-r--r--core/java/android/view/ViewPropertyAnimator.java21
-rw-r--r--core/java/android/view/ViewRootImpl.java30
-rw-r--r--core/java/android/view/WindowlessViewRoot.java3
-rw-r--r--core/java/android/view/WindowlessWindowManager.java17
-rw-r--r--core/java/android/view/accessibility/AccessibilityCache.java204
-rw-r--r--core/java/android/view/accessibility/AccessibilityInteractionClient.java13
-rw-r--r--core/java/android/view/animation/Animation.java12
-rw-r--r--core/java/android/view/contentcapture/ChildContentCaptureSession.java5
-rw-r--r--core/java/android/view/contentcapture/ContentCaptureSession.java13
-rw-r--r--core/java/android/view/contentcapture/MainContentCaptureSession.java10
-rw-r--r--core/java/android/widget/Editor.java3
-rw-r--r--core/java/android/widget/ListView.java2
-rw-r--r--core/java/android/widget/ScrollView.java6
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java158
-rw-r--r--core/java/com/android/internal/compat/ChangeReporter.java73
-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.java87
-rw-r--r--core/java/com/android/internal/policy/DecorView.java4
-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_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_os_SystemProperties.cpp2
-rw-r--r--core/jni/android_view_InputChannel.cpp82
-rw-r--r--core/jni/android_view_InputEventReceiver.cpp2
-rw-r--r--core/jni/com_android_internal_os_Zygote.cpp2
-rw-r--r--core/proto/android/app/settings_enums.proto4
-rw-r--r--core/proto/android/server/windowmanagerservice.proto2
-rw-r--r--core/res/AndroidManifest.xml6
-rw-r--r--core/res/res/drawable/media_seamless_background.xml (renamed from packages/SystemUI/res/drawable/biometric_dialog_bg.xml)22
-rw-r--r--core/res/res/layout/notification_material_media_transfer_action.xml39
-rw-r--r--core/res/res/layout/notification_template_header.xml4
-rw-r--r--core/res/res/layout/notification_template_material_big_media.xml4
-rw-r--r--core/res/res/values-af/strings.xml6
-rw-r--r--core/res/res/values-am/strings.xml6
-rw-r--r--core/res/res/values-ar/strings.xml10
-rw-r--r--core/res/res/values-as/strings.xml6
-rw-r--r--core/res/res/values-az/strings.xml6
-rw-r--r--core/res/res/values-b+sr+Latn/strings.xml6
-rw-r--r--core/res/res/values-be/strings.xml14
-rw-r--r--core/res/res/values-bg/strings.xml6
-rw-r--r--core/res/res/values-bn/strings.xml6
-rw-r--r--core/res/res/values-bs/strings.xml10
-rw-r--r--core/res/res/values-ca/strings.xml6
-rw-r--r--core/res/res/values-cs/strings.xml6
-rw-r--r--core/res/res/values-da/strings.xml6
-rw-r--r--core/res/res/values-de/strings.xml10
-rw-r--r--core/res/res/values-el/strings.xml8
-rw-r--r--core/res/res/values-en-rAU/strings.xml24
-rw-r--r--core/res/res/values-en-rCA/strings.xml24
-rw-r--r--core/res/res/values-en-rGB/strings.xml24
-rw-r--r--core/res/res/values-en-rIN/strings.xml24
-rw-r--r--core/res/res/values-en-rXC/strings.xml6
-rw-r--r--core/res/res/values-es-rUS/strings.xml6
-rw-r--r--core/res/res/values-es/strings.xml14
-rw-r--r--core/res/res/values-et/strings.xml10
-rw-r--r--core/res/res/values-eu/strings.xml8
-rw-r--r--core/res/res/values-fa/strings.xml22
-rw-r--r--core/res/res/values-fi/strings.xml8
-rw-r--r--core/res/res/values-fr-rCA/strings.xml14
-rw-r--r--core/res/res/values-fr/strings.xml8
-rw-r--r--core/res/res/values-gl/strings.xml28
-rw-r--r--core/res/res/values-gu/strings.xml8
-rw-r--r--core/res/res/values-hi/strings.xml8
-rw-r--r--core/res/res/values-hr/strings.xml8
-rw-r--r--core/res/res/values-hu/strings.xml6
-rw-r--r--core/res/res/values-hy/strings.xml14
-rw-r--r--core/res/res/values-in/strings.xml8
-rw-r--r--core/res/res/values-is/strings.xml6
-rw-r--r--core/res/res/values-it/strings.xml6
-rw-r--r--core/res/res/values-iw/strings.xml8
-rw-r--r--core/res/res/values-ja/strings.xml8
-rw-r--r--core/res/res/values-ka/strings.xml6
-rw-r--r--core/res/res/values-kk/strings.xml6
-rw-r--r--core/res/res/values-km/strings.xml6
-rw-r--r--core/res/res/values-kn/strings.xml8
-rw-r--r--core/res/res/values-ko/strings.xml12
-rw-r--r--core/res/res/values-ky/strings.xml12
-rw-r--r--core/res/res/values-lo/strings.xml6
-rw-r--r--core/res/res/values-lt/strings.xml6
-rw-r--r--core/res/res/values-lv/strings.xml6
-rw-r--r--core/res/res/values-mk/strings.xml10
-rw-r--r--core/res/res/values-ml/strings.xml6
-rw-r--r--core/res/res/values-mn/strings.xml6
-rw-r--r--core/res/res/values-mr/strings.xml8
-rw-r--r--core/res/res/values-ms/strings.xml8
-rw-r--r--core/res/res/values-my/strings.xml8
-rw-r--r--core/res/res/values-nb/strings.xml8
-rw-r--r--core/res/res/values-ne/strings.xml6
-rw-r--r--core/res/res/values-nl/strings.xml6
-rw-r--r--core/res/res/values-or/strings.xml6
-rw-r--r--core/res/res/values-pa/strings.xml6
-rw-r--r--core/res/res/values-pl/strings.xml8
-rw-r--r--core/res/res/values-pt-rBR/strings.xml12
-rw-r--r--core/res/res/values-pt-rPT/strings.xml8
-rw-r--r--core/res/res/values-pt/strings.xml12
-rw-r--r--core/res/res/values-ro/strings.xml6
-rw-r--r--core/res/res/values-ru/strings.xml12
-rw-r--r--core/res/res/values-si/strings.xml6
-rw-r--r--core/res/res/values-sk/strings.xml8
-rw-r--r--core/res/res/values-sl/strings.xml18
-rw-r--r--core/res/res/values-sq/strings.xml6
-rw-r--r--core/res/res/values-sr/strings.xml6
-rw-r--r--core/res/res/values-sv/strings.xml6
-rw-r--r--core/res/res/values-sw/strings.xml6
-rw-r--r--core/res/res/values-ta/strings.xml28
-rw-r--r--core/res/res/values-te/strings.xml14
-rw-r--r--core/res/res/values-th/strings.xml8
-rw-r--r--core/res/res/values-tl/strings.xml6
-rw-r--r--core/res/res/values-tr/strings.xml6
-rw-r--r--core/res/res/values-uk/strings.xml6
-rw-r--r--core/res/res/values-ur/strings.xml6
-rw-r--r--core/res/res/values-uz/strings.xml10
-rw-r--r--core/res/res/values-vi/strings.xml10
-rw-r--r--core/res/res/values-zh-rCN/strings.xml10
-rw-r--r--core/res/res/values-zh-rHK/strings.xml6
-rw-r--r--core/res/res/values-zh-rTW/strings.xml6
-rw-r--r--core/res/res/values-zu/strings.xml6
-rw-r--r--core/res/res/values/strings.xml4
-rw-r--r--core/res/res/values/symbols.xml2
-rw-r--r--core/tests/coretests/Android.bp1
-rw-r--r--core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java218
-rw-r--r--core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java5
-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.java217
-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--graphics/java/android/graphics/drawable/GradientDrawable.java119
-rw-r--r--keystore/java/android/security/Credentials.java17
-rw-r--r--keystore/java/android/security/IKeyChainService.aidl3
-rw-r--r--libs/hwui/pipeline/skia/SkiaPipeline.cpp1
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp2
-rw-r--r--libs/hwui/renderthread/ReliableSurface.h2
-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/Android.bp2
-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/java/android/media/ExifInterface.java32
-rw-r--r--media/jni/android_media_MediaCodec.cpp2
-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.h2
-rw-r--r--media/lib/signer/Android.bp2
-rw-r--r--media/native/midi/Android.bp2
-rw-r--r--media/tests/audiotests/shared_mem_test.cpp2
-rw-r--r--packages/BackupEncryption/proto/backup_chunks_metadata.proto (renamed from core/proto/android/server/backup_chunks_metadata.proto)4
-rw-r--r--packages/BackupEncryption/proto/key_value_listing.proto40
-rw-r--r--packages/BackupEncryption/proto/key_value_pair.proto31
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/CryptoSettings.java242
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/Chunk.java70
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/ChunkListingMap.java47
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/ChunkOrderingType.java6
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoder.java2
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoder.java2
-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/kv/DecryptedChunkKvOutput.java111
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/kv/KeyValueListingBuilder.java77
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/DecryptedChunkOutput.java8
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/StartSecondaryKeyRotationTask.java104
-rw-r--r--packages/BackupEncryption/test/robolectric/Android.bp5
-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/chunk/ChunkListingMapTest.java181
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunk/ChunkTest.java122
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java2
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java2
-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/kv/DecryptedChunkKvOutputTest.java164
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/kv/KeyValueListingBuilderTest.java110
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/StartSecondaryKeyRotationTaskTest.java113
-rw-r--r--packages/BackupRestoreConfirmation/res/values-in/strings.xml8
-rw-r--r--packages/CarSystemUI/Android.bp43
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java2
-rw-r--r--packages/CarrierDefaultApp/tests/unit/Android.bp1
-rw-r--r--packages/PackageInstaller/AndroidManifest.xml2
-rw-r--r--packages/SettingsLib/SearchWidget/res/values-km/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-ca/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-hy/strings.xml12
-rw-r--r--packages/SettingsLib/res/values-in/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-it/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-iw/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-ko/arrays.xml2
-rw-r--r--packages/SettingsLib/res/values-lo/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-lv/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-mk/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-ne/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-or/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-pa/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-pt-rPT/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-sk/strings.xml12
-rw-r--r--packages/SettingsLib/res/values-sl/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-ta/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-th/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-uz/arrays.xml2
-rw-r--r--packages/SettingsLib/res/values-vi/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-zh-rCN/strings.xml2
-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/src/com/android/shell/BugreportProgressService.java4
-rw-r--r--packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java21
-rw-r--r--packages/SystemUI/Android.bp10
-rw-r--r--packages/SystemUI/legacy/recents/res/values-vi/strings.xml2
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java2
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java8
-rw-r--r--packages/SystemUI/res-keyguard/values-et/strings.xml2
-rw-r--r--packages/SystemUI/res/layout/auth_biometric_contents.xml2
-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_tile_detail_text.xml33
-rw-r--r--packages/SystemUI/res/values-af/strings.xml3
-rw-r--r--packages/SystemUI/res/values-am/strings.xml3
-rw-r--r--packages/SystemUI/res/values-ar/strings.xml7
-rw-r--r--packages/SystemUI/res/values-as/strings.xml3
-rw-r--r--packages/SystemUI/res/values-az/strings.xml3
-rw-r--r--packages/SystemUI/res/values-b+sr+Latn/strings.xml3
-rw-r--r--packages/SystemUI/res/values-be/strings.xml3
-rw-r--r--packages/SystemUI/res/values-bg/strings.xml5
-rw-r--r--packages/SystemUI/res/values-bn/strings.xml3
-rw-r--r--packages/SystemUI/res/values-bs/strings.xml3
-rw-r--r--packages/SystemUI/res/values-ca/strings.xml3
-rw-r--r--packages/SystemUI/res/values-cs/strings.xml3
-rw-r--r--packages/SystemUI/res/values-da/strings.xml5
-rw-r--r--packages/SystemUI/res/values-de/strings.xml3
-rw-r--r--packages/SystemUI/res/values-el/strings.xml3
-rw-r--r--packages/SystemUI/res/values-en-rAU/strings.xml5
-rw-r--r--packages/SystemUI/res/values-en-rCA/strings.xml5
-rw-r--r--packages/SystemUI/res/values-en-rGB/strings.xml5
-rw-r--r--packages/SystemUI/res/values-en-rIN/strings.xml5
-rw-r--r--packages/SystemUI/res/values-en-rXC/strings.xml3
-rw-r--r--packages/SystemUI/res/values-es-rUS/strings.xml3
-rw-r--r--packages/SystemUI/res/values-es/strings.xml5
-rw-r--r--packages/SystemUI/res/values-et/strings.xml9
-rw-r--r--packages/SystemUI/res/values-eu/strings.xml3
-rw-r--r--packages/SystemUI/res/values-fa/strings.xml15
-rw-r--r--packages/SystemUI/res/values-fi/strings.xml3
-rw-r--r--packages/SystemUI/res/values-fr-rCA/strings.xml3
-rw-r--r--packages/SystemUI/res/values-fr/strings.xml3
-rw-r--r--packages/SystemUI/res/values-gl/strings.xml3
-rw-r--r--packages/SystemUI/res/values-gu/strings.xml3
-rw-r--r--packages/SystemUI/res/values-hi/strings.xml5
-rw-r--r--packages/SystemUI/res/values-hr/strings.xml3
-rw-r--r--packages/SystemUI/res/values-hu/strings.xml3
-rw-r--r--packages/SystemUI/res/values-hy/strings.xml13
-rw-r--r--packages/SystemUI/res/values-in/strings.xml3
-rw-r--r--packages/SystemUI/res/values-is/strings.xml3
-rw-r--r--packages/SystemUI/res/values-it/strings.xml3
-rw-r--r--packages/SystemUI/res/values-iw/strings.xml7
-rw-r--r--packages/SystemUI/res/values-ja/strings.xml3
-rw-r--r--packages/SystemUI/res/values-ka/strings.xml3
-rw-r--r--packages/SystemUI/res/values-kk/strings.xml3
-rw-r--r--packages/SystemUI/res/values-km/strings.xml7
-rw-r--r--packages/SystemUI/res/values-kn/strings.xml5
-rw-r--r--packages/SystemUI/res/values-ko/strings.xml7
-rw-r--r--packages/SystemUI/res/values-ky/strings.xml7
-rw-r--r--packages/SystemUI/res/values-lo/strings.xml3
-rw-r--r--packages/SystemUI/res/values-lt/strings.xml5
-rw-r--r--packages/SystemUI/res/values-lv/strings.xml3
-rw-r--r--packages/SystemUI/res/values-mk/strings.xml11
-rw-r--r--packages/SystemUI/res/values-ml/strings.xml3
-rw-r--r--packages/SystemUI/res/values-mn/strings.xml3
-rw-r--r--packages/SystemUI/res/values-mr/strings.xml3
-rw-r--r--packages/SystemUI/res/values-ms/strings.xml3
-rw-r--r--packages/SystemUI/res/values-my/strings.xml3
-rw-r--r--packages/SystemUI/res/values-nb/strings.xml3
-rw-r--r--packages/SystemUI/res/values-ne/strings.xml5
-rw-r--r--packages/SystemUI/res/values-nl/strings.xml3
-rw-r--r--packages/SystemUI/res/values-or/strings.xml3
-rw-r--r--packages/SystemUI/res/values-pa/strings.xml5
-rw-r--r--packages/SystemUI/res/values-pl/strings.xml3
-rw-r--r--packages/SystemUI/res/values-pt-rBR/strings.xml5
-rw-r--r--packages/SystemUI/res/values-pt-rPT/strings.xml3
-rw-r--r--packages/SystemUI/res/values-pt/strings.xml5
-rw-r--r--packages/SystemUI/res/values-ro/strings.xml3
-rw-r--r--packages/SystemUI/res/values-ru/strings.xml9
-rw-r--r--packages/SystemUI/res/values-si/strings.xml3
-rw-r--r--packages/SystemUI/res/values-sk/strings.xml5
-rw-r--r--packages/SystemUI/res/values-sl/strings.xml3
-rw-r--r--packages/SystemUI/res/values-sq/strings.xml5
-rw-r--r--packages/SystemUI/res/values-sr/strings.xml3
-rw-r--r--packages/SystemUI/res/values-sv/strings.xml3
-rw-r--r--packages/SystemUI/res/values-sw/strings.xml3
-rw-r--r--packages/SystemUI/res/values-ta/strings.xml11
-rw-r--r--packages/SystemUI/res/values-te/strings.xml3
-rw-r--r--packages/SystemUI/res/values-th/strings.xml3
-rw-r--r--packages/SystemUI/res/values-tl/strings.xml3
-rw-r--r--packages/SystemUI/res/values-tr/strings.xml3
-rw-r--r--packages/SystemUI/res/values-uk/strings.xml3
-rw-r--r--packages/SystemUI/res/values-ur/strings.xml3
-rw-r--r--packages/SystemUI/res/values-uz/strings.xml5
-rw-r--r--packages/SystemUI/res/values-vi/strings.xml3
-rw-r--r--packages/SystemUI/res/values-zh-rCN/strings.xml5
-rw-r--r--packages/SystemUI/res/values-zh-rHK/strings.xml3
-rw-r--r--packages/SystemUI/res/values-zh-rTW/strings.xml3
-rw-r--r--packages/SystemUI/res/values-zu/strings.xml3
-rw-r--r--packages/SystemUI/res/values/config.xml3
-rw-r--r--packages/SystemUI/res/values/dimens.xml1
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java8
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java115
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java4
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java6
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java3
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java2
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java8
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/ActivityBinder.java44
-rw-r--r--packages/SystemUI/src/com/android/systemui/ComponentBinder.java (renamed from core/java/android/view/Transaction.aidl)16
-rw-r--r--packages/SystemUI/src/com/android/systemui/ContextComponentHelper.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/ContextComponentResolver.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/CornerHandleView.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/Dependency.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/DependencyProvider.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/ForegroundServicesDialog.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/ServiceBinder.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIBinder.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIFactory.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIModule.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIRootComponent.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIService.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistHandleLikeHomeBehavior.java45
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java51
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java31
-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/bubbles/BubbleController.java63
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java47
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java76
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java59
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/brightline/DiagonalClassifier.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/brightline/DistanceClassifier.java87
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/brightline/PointerCountClassifier.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/brightline/TypeClassifier.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java138
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java154
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java56
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java68
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java48
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java15
-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.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java17
-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/MediaTransferManager.java48
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java14
-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.java104
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java18
-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.java41
-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/BiometricUnlockController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java19
-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.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplies.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java (renamed from packages/SystemUI/src/com/android/systemui/util/AsyncSensorManager.java)26
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java305
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceNotificationListenerTest.java26
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleLikeHomeBehaviorTest.java158
-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/doze/DozeConfigurationUtil.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.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.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryHelper.java21
-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.java32
-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.java15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/SbnBuilder.java16
-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.java4
-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.java278
-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/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/NotificationPanelViewTest.java2
-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/policy/BluetoothControllerImplTest.java41
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/sensors/AsyncSensorManagerTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/util/AsyncSensorManagerTest.java)40
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java53
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeSensorManager.java (renamed from packages/SystemUI/tests/src/com/android/systemui/utils/hardware/FakeSensorManager.java)16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorTest.java218
-rw-r--r--proto/src/gnss.proto21
-rw-r--r--proto/src/metrics_constants/metrics_constants.proto11
-rw-r--r--proto/src/wifi.proto19
-rw-r--r--services/Android.bp5
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java10
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java22
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/AccessibilityGestureDetector.java16
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java309
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java290
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java127
-rw-r--r--services/backup/Android.bp1
-rw-r--r--services/backup/backuplib/Android.bp5
-rw-r--r--services/backup/backuplib/java/com/android/server/backup/TransportManager.java (renamed from services/backup/java/com/android/server/backup/TransportManager.java)0
-rw-r--r--services/backup/backuplib/java/com/android/server/backup/transport/OnTransportRegisteredListener.java (renamed from services/backup/java/com/android/server/backup/transport/OnTransportRegisteredListener.java)0
-rw-r--r--services/backup/backuplib/java/com/android/server/backup/transport/TransportClient.java (renamed from services/backup/java/com/android/server/backup/transport/TransportClient.java)0
-rw-r--r--services/backup/backuplib/java/com/android/server/backup/transport/TransportClientManager.java (renamed from services/backup/java/com/android/server/backup/transport/TransportClientManager.java)0
-rw-r--r--services/backup/backuplib/java/com/android/server/backup/transport/TransportConnectionListener.java (renamed from services/backup/java/com/android/server/backup/transport/TransportConnectionListener.java)0
-rw-r--r--services/backup/backuplib/java/com/android/server/backup/transport/TransportNotAvailableException.java (renamed from services/backup/java/com/android/server/backup/transport/TransportNotAvailableException.java)0
-rw-r--r--services/backup/backuplib/java/com/android/server/backup/transport/TransportNotRegisteredException.java (renamed from services/backup/java/com/android/server/backup/transport/TransportNotRegisteredException.java)0
-rw-r--r--services/backup/backuplib/java/com/android/server/backup/transport/TransportStats.java (renamed from services/backup/java/com/android/server/backup/transport/TransportStats.java)0
-rw-r--r--services/backup/backuplib/java/com/android/server/backup/transport/TransportUtils.java (renamed from services/backup/java/com/android/server/backup/transport/TransportUtils.java)0
-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/MountServiceIdler.java18
-rw-r--r--services/core/java/com/android/server/PackageWatchdog.java50
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java9
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java24
-rw-r--r--services/core/java/com/android/server/am/MemoryStatUtil.java141
-rw-r--r--services/core/java/com/android/server/am/OWNERS1
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java36
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceBroker.java503
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java204
-rw-r--r--services/core/java/com/android/server/audio/BtHelper.java30
-rw-r--r--services/core/java/com/android/server/audio/TEST_MAPPING15
-rw-r--r--services/core/java/com/android/server/biometrics/BiometricService.java20
-rw-r--r--services/core/java/com/android/server/camera/CameraServiceProxy.java2
-rw-r--r--services/core/java/com/android/server/compat/PlatformCompat.java21
-rw-r--r--services/core/java/com/android/server/display/color/ColorDisplayService.java9
-rw-r--r--services/core/java/com/android/server/display/color/DisplayWhiteBalanceTintController.java27
-rw-r--r--services/core/java/com/android/server/display/color/TintController.java26
-rw-r--r--services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java14
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java140
-rw-r--r--services/core/java/com/android/server/integrity/OWNERS6
-rw-r--r--services/core/java/com/android/server/location/AbstractLocationProvider.java52
-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/NotificationComparator.java6
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java46
-rw-r--r--services/core/java/com/android/server/om/OverlayManagerService.java2
-rw-r--r--services/core/java/com/android/server/om/OverlayManagerShellCommand.java106
-rw-r--r--services/core/java/com/android/server/pm/AppsFilter.java165
-rw-r--r--services/core/java/com/android/server/pm/InstantAppRegistry.java2
-rw-r--r--services/core/java/com/android/server/pm/LauncherAppsService.java54
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java1
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java513
-rw-r--r--services/core/java/com/android/server/pm/Settings.java12
-rw-r--r--services/core/java/com/android/server/pm/StagingManager.java190
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java48
-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/power/PowerManagerService.java26
-rw-r--r--services/core/java/com/android/server/rollback/AppDataRollbackHelper.java178
-rw-r--r--services/core/java/com/android/server/rollback/Rollback.java121
-rw-r--r--services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java512
-rw-r--r--services/core/java/com/android/server/rollback/RollbackStore.java3
-rw-r--r--services/core/java/com/android/server/security/VerityUtils.java188
-rw-r--r--services/core/java/com/android/server/stats/IonMemoryUtil.java167
-rw-r--r--services/core/java/com/android/server/stats/ProcfsMemoryUtil.java113
-rw-r--r--services/core/java/com/android/server/stats/StatsCompanionService.java68
-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.java10
-rw-r--r--services/core/java/com/android/server/wm/ActivityDisplay.java27
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java19
-rw-r--r--services/core/java/com/android/server/wm/ActivityStack.java28
-rw-r--r--services/core/java/com/android/server/wm/ActivityStackSupervisor.java25
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java56
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java86
-rw-r--r--services/core/java/com/android/server/wm/AnimationAdapter.java6
-rw-r--r--services/core/java/com/android/server/wm/AppTransition.java2
-rw-r--r--services/core/java/com/android/server/wm/AppWindowToken.java28
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java60
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java4
-rw-r--r--services/core/java/com/android/server/wm/DockedStackDividerController.java11
-rw-r--r--services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java1
-rw-r--r--services/core/java/com/android/server/wm/InsetsSourceProvider.java32
-rw-r--r--services/core/java/com/android/server/wm/InsetsStateController.java61
-rw-r--r--services/core/java/com/android/server/wm/KeyguardController.java16
-rw-r--r--services/core/java/com/android/server/wm/LaunchParamsController.java4
-rw-r--r--services/core/java/com/android/server/wm/LocalAnimationAdapter.java12
-rw-r--r--services/core/java/com/android/server/wm/LockTaskController.java7
-rw-r--r--services/core/java/com/android/server/wm/PinnedStackController.java146
-rw-r--r--services/core/java/com/android/server/wm/PointerEventDispatcher.java3
-rw-r--r--services/core/java/com/android/server/wm/RecentsAnimation.java8
-rw-r--r--services/core/java/com/android/server/wm/RecentsAnimationController.java84
-rw-r--r--services/core/java/com/android/server/wm/RemoteAnimationController.java50
-rw-r--r--services/core/java/com/android/server/wm/RootActivityContainer.java54
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java3
-rw-r--r--services/core/java/com/android/server/wm/Session.java15
-rw-r--r--services/core/java/com/android/server/wm/SurfaceAnimator.java3
-rw-r--r--services/core/java/com/android/server/wm/Task.java5
-rw-r--r--services/core/java/com/android/server/wm/TaskPositioner.java12
-rw-r--r--services/core/java/com/android/server/wm/TaskRecord.java12
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotPersister.java2
-rw-r--r--services/core/java/com/android/server/wm/TaskStack.java121
-rw-r--r--services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java179
-rw-r--r--services/core/java/com/android/server/wm/WallpaperController.java1
-rw-r--r--services/core/java/com/android/server/wm/WallpaperWindowToken.java8
-rw-r--r--services/core/java/com/android/server/wm/WindowAnimationSpec.java6
-rw-r--r--services/core/java/com/android/server/wm/WindowAnimator.java19
-rw-r--r--services/core/java/com/android/server/wm/WindowChangeAnimationSpec.java5
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java6
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java287
-rw-r--r--services/core/java/com/android/server/wm/WindowProcessListener.java1
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java55
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java56
-rw-r--r--services/core/java/com/android/server/wm/WindowSurfacePlacer.java48
-rw-r--r--services/core/jni/Android.bp5
-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/core/jni/com_android_server_security_VerityUtils.cpp177
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java246
-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/gestures/AccessibilityGestureDetectorTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java145
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java16
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java33
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ModuleInfoProviderTest.java9
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java15
-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/AppDataRollbackHelperTest.java14
-rw-r--r--services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java65
-rw-r--r--services/tests/servicestests/src/com/android/server/stats/IonMemoryUtilTest.java158
-rw-r--r--services/tests/servicestests/src/com/android/server/stats/ProcfsMemoryUtilTest.java124
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java15
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java15
-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.java5
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java29
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java1
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java1
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java32
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java111
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java79
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java37
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java13
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java2
-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--services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java33
-rw-r--r--services/wifi/java/android/net/wifi/WifiStackClient.java22
-rw-r--r--startop/apps/test/Android.bp3
-rw-r--r--startop/apps/test/src/CPUIntensive.java67
-rw-r--r--startop/apps/test/src/SystemServerBenchmarkActivity.java66
-rw-r--r--startop/view_compiler/Android.bp1
-rw-r--r--telecomm/java/android/telecom/CallScreeningService.java6
-rw-r--r--telecomm/java/android/telecom/ConnectionService.java10
-rw-r--r--telecomm/java/android/telecom/Logging/SessionManager.java14
-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/SubscriptionInfo.java3
-rw-r--r--telephony/java/android/telephony/SubscriptionManager.java3
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java86
-rw-r--r--telephony/java/android/telephony/UiccAccessRule.java20
-rw-r--r--telephony/java/android/telephony/ims/ImsCallProfile.java59
-rw-r--r--telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl7
-rw-r--r--telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java176
-rw-r--r--telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl2
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl7
-rw-r--r--telephony/java/com/android/internal/telephony/TelephonyProperties.java7
-rw-r--r--telephony/java/com/google/android/mms/ContentType.java247
-rw-r--r--telephony/java/com/google/android/mms/InvalidHeaderValueException.java44
-rw-r--r--telephony/java/com/google/android/mms/MmsException.java66
-rwxr-xr-xtelephony/java/com/google/android/mms/package.html5
-rw-r--r--telephony/java/com/google/android/mms/pdu/AcknowledgeInd.java95
-rw-r--r--telephony/java/com/google/android/mms/pdu/Base64.java170
-rw-r--r--telephony/java/com/google/android/mms/pdu/CharacterSets.java176
-rw-r--r--telephony/java/com/google/android/mms/pdu/DeliveryInd.java145
-rw-r--r--telephony/java/com/google/android/mms/pdu/EncodedStringValue.java298
-rw-r--r--telephony/java/com/google/android/mms/pdu/GenericPdu.java122
-rw-r--r--telephony/java/com/google/android/mms/pdu/MultimediaMessagePdu.java164
-rw-r--r--telephony/java/com/google/android/mms/pdu/NotificationInd.java307
-rw-r--r--telephony/java/com/google/android/mms/pdu/NotifyRespInd.java121
-rw-r--r--telephony/java/com/google/android/mms/pdu/PduBody.java204
-rw-r--r--telephony/java/com/google/android/mms/pdu/PduComposer.java1229
-rw-r--r--telephony/java/com/google/android/mms/pdu/PduContentTypes.java113
-rw-r--r--telephony/java/com/google/android/mms/pdu/PduHeaders.java733
-rwxr-xr-xtelephony/java/com/google/android/mms/pdu/PduParser.java2023
-rw-r--r--telephony/java/com/google/android/mms/pdu/PduPart.java439
-rwxr-xr-xtelephony/java/com/google/android/mms/pdu/PduPersister.java1573
-rw-r--r--telephony/java/com/google/android/mms/pdu/QuotedPrintable.java71
-rw-r--r--telephony/java/com/google/android/mms/pdu/ReadOrigInd.java158
-rw-r--r--telephony/java/com/google/android/mms/pdu/ReadRecInd.java150
-rw-r--r--telephony/java/com/google/android/mms/pdu/RetrieveConf.java324
-rw-r--r--telephony/java/com/google/android/mms/pdu/SendConf.java124
-rw-r--r--telephony/java/com/google/android/mms/pdu/SendReq.java370
-rwxr-xr-xtelephony/java/com/google/android/mms/pdu/package.html5
-rw-r--r--telephony/java/com/google/android/mms/util/AbstractCache.java119
-rw-r--r--telephony/java/com/google/android/mms/util/DownloadDrmHelper.java115
-rw-r--r--telephony/java/com/google/android/mms/util/DrmConvertSession.java177
-rw-r--r--telephony/java/com/google/android/mms/util/PduCache.java268
-rw-r--r--telephony/java/com/google/android/mms/util/PduCacheEntry.java50
-rw-r--r--telephony/java/com/google/android/mms/util/SqliteWrapper.java128
-rwxr-xr-xtelephony/java/com/google/android/mms/util/package.html5
-rw-r--r--test-mock/Android.bp2
-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/src/com/android/server/PackageWatchdogTest.java135
-rw-r--r--tools/aapt2/Debug.cpp92
-rw-r--r--tools/aapt2/Debug.h1
-rw-r--r--tools/aapt2/Main.cpp5
-rw-r--r--tools/aapt2/cmd/Dump.cpp11
-rw-r--r--tools/aapt2/cmd/Dump.h12
-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
-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/WifiScanner.java5
791 files changed, 27318 insertions, 9497 deletions
diff --git a/Android.bp b/Android.bp
index 35c7b1bc6fdd..31dbaadfd332 100644
--- a/Android.bp
+++ b/Android.bp
@@ -163,7 +163,7 @@ filegroup {
}
filegroup {
- name: "framework-srcs",
+ name: "framework-non-updatable-sources",
srcs: [
// Java/AIDL sources under frameworks/base
":framework-core-sources",
@@ -211,6 +211,14 @@ filegroup {
],
}
+filegroup {
+ name: "framework-all-sources",
+ srcs: [
+ ":framework-non-updatable-sources",
+ ":updatable-media-srcs",
+ ],
+}
+
java_defaults {
name: "framework-aidl-export-defaults",
aidl: {
@@ -288,15 +296,12 @@ java_defaults {
defaults: ["framework-aidl-export-defaults"],
installable: true,
- srcs: [
- ":framework-srcs",
- "core/java/**/*.logtags",
- ],
-
aidl: {
generate_get_transaction_name: true,
},
+ srcs: ["core/java/**/*.logtags"],
+
exclude_srcs: [
// See comment on framework-atb-backward-compatibility module below
"core/java/android/content/pm/AndroidTestBaseUpdater.java",
@@ -359,6 +364,7 @@ filegroup {
java_library {
name: "framework-minus-apex",
defaults: ["framework-defaults"],
+ srcs: [":framework-non-updatable-sources"],
javac_shard_size: 150,
}
@@ -379,8 +385,16 @@ java_library {
}
java_library {
+ name: "framework-all",
+ defaults: ["framework-defaults"],
+ srcs: [":framework-all-sources"],
+ installable: false,
+}
+
+java_library {
name: "framework-annotation-proc",
defaults: ["framework-defaults"],
+ srcs: [":framework-all-sources"],
installable: false,
plugins: [
"unsupportedappusage-annotation-processor",
@@ -883,10 +897,21 @@ metalava_framework_docs_args += " --replace-documentation " +
// replacement (with $1, $2 backreferences to the regex groups)
"'$$1https://docs.oracle.com/javase/8/docs/$$2\">' "
+packages_to_document = [
+ "android",
+ "java",
+ "javax",
+ "org.apache.http",
+ "org.json",
+ "org.w3c.dom",
+ "org.xml.sax",
+ "org.xmlpull",
+]
+
stubs_defaults {
name: "framework-doc-stubs-default",
srcs: [
- ":framework-srcs",
+ ":framework-non-updatable-sources",
"core/java/**/*.logtags",
"test-base/src/**/*.java",
":opt-telephony-srcs",
@@ -906,7 +931,7 @@ stubs_defaults {
"sdk-dir",
"api-versions-jars-dir",
],
- previous_api: ":last-released-public-api",
+ previous_api: ":last-released-public-api-for-metalava-annotations",
merge_annotations_dirs: [
"metalava-manual",
"ojluni-annotated-sdk-stubs",
@@ -951,7 +976,7 @@ doc_defaults {
stubs_defaults {
name: "metalava-api-stubs-default",
srcs: [
- ":framework-srcs",
+ ":framework-non-updatable-sources",
"core/java/**/*.logtags",
":opt-telephony-srcs",
":opt-net-voip-srcs",
@@ -963,7 +988,7 @@ stubs_defaults {
local_sourcepaths: frameworks_base_subdirs,
installable: false,
annotations_enabled: true,
- previous_api: ":last-released-public-api",
+ previous_api: ":last-released-public-api-for-metalava-annotations",
merge_annotations_dirs: [
"metalava-manual",
"ojluni-annotated-sdk-stubs",
@@ -974,6 +999,7 @@ stubs_defaults {
"api-versions-jars-dir",
],
sdk_version: "core_platform",
+ filter_packages: packages_to_document,
}
droidstubs {
@@ -1298,7 +1324,7 @@ droidstubs {
installable: false,
sdk_version: "core_platform",
annotations_enabled: true,
- previous_api: ":last-released-public-api",
+ previous_api: ":last-released-public-api-for-metalava-annotations",
merge_annotations_dirs: [
"metalava-manual",
"ojluni-annotated-sdk-stubs",
@@ -1472,7 +1498,7 @@ filegroup {
// annotations to private apis
aidl_mapping {
name: "framework-aidl-mappings",
- srcs: [":framework-srcs"],
+ srcs: [":framework-all-sources"],
output: "framework-aidl-mappings.txt",
}
@@ -1485,3 +1511,25 @@ genrule {
targets: ["droidcore"],
},
}
+
+// Avoid including Parcelable classes as we don't want to have two copies of
+// Parcelable cross the process.
+filegroup {
+ name: "framework-telephony-stack-shared-srcs",
+ srcs: [
+ "core/java/android/os/RegistrantList.java",
+ "core/java/android/os/Registrant.java",
+ "core/java/android/util/LocalLog.java",
+ "core/java/android/util/Slog.java",
+ "core/java/android/util/TimeUtils.java",
+ "core/java/com/android/internal/os/SomeArgs.java",
+ "core/java/com/android/internal/util/Preconditions.java",
+ "core/java/com/android/internal/util/State.java",
+ "core/java/com/android/internal/util/StateMachine.java",
+ "core/java/com/android/internal/util/XmlUtils.java",
+ "core/java/com/android/internal/util/HexDump.java",
+ "core/java/com/android/internal/util/IndentingPrintWriter.java",
+ "core/java/com/android/internal/util/DumpUtils.java"
+ ],
+}
+
diff --git a/apct-tests/perftests/core/src/android/wm/RecentsAnimationPerfTest.java b/apct-tests/perftests/core/src/android/wm/RecentsAnimationPerfTest.java
index 2b6f4557303b..9cfc3d272145 100644
--- a/apct-tests/perftests/core/src/android/wm/RecentsAnimationPerfTest.java
+++ b/apct-tests/perftests/core/src/android/wm/RecentsAnimationPerfTest.java
@@ -188,8 +188,8 @@ public class RecentsAnimationPerfTest extends WindowManagerPerfTestBase {
@Override
public void onAnimationStart(IRecentsAnimationController controller,
- RemoteAnimationTarget[] apps, Rect homeContentInsets,
- Rect minimizedHomeBounds) throws RemoteException {
+ RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
+ Rect homeContentInsets, Rect minimizedHomeBounds) throws RemoteException {
final Pair<String, Boolean> finishCase = finishCases.get(mIteration++ % 2);
final boolean moveRecentsToTop = finishCase.second;
makeInterval();
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 a7c6891c3d7f..d688220fd17b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2812,12 +2812,12 @@ package android.accessibilityservice {
method public void onClicked(android.accessibilityservice.AccessibilityButtonController);
}
- public final class AccessibilityGestureInfo implements android.os.Parcelable {
+ public final class AccessibilityGestureEvent implements android.os.Parcelable {
method public int describeContents();
method public int getDisplayId();
method public int getGestureId();
method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.accessibilityservice.AccessibilityGestureInfo> CREATOR;
+ field @NonNull public static final android.os.Parcelable.Creator<android.accessibilityservice.AccessibilityGestureEvent> CREATOR;
}
public abstract class AccessibilityService extends android.app.Service {
@@ -2836,7 +2836,7 @@ package android.accessibilityservice {
method public abstract void onAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
method public final android.os.IBinder onBind(android.content.Intent);
method @Deprecated protected boolean onGesture(int);
- method public boolean onGesture(@NonNull android.accessibilityservice.AccessibilityGestureInfo);
+ method public boolean onGesture(@NonNull android.accessibilityservice.AccessibilityGestureEvent);
method public abstract void onInterrupt();
method protected boolean onKeyEvent(android.view.KeyEvent);
method protected void onServiceConnected();
@@ -23968,6 +23968,7 @@ package android.media {
method @Nullable public long[] getThumbnailRange();
method public boolean hasAttribute(@NonNull String);
method public boolean hasThumbnail();
+ method public static boolean isSupportedMimeType(@NonNull String);
method public boolean isThumbnailCompressed();
method public void saveAttributes() throws java.io.IOException;
method public void setAttribute(@NonNull String, @Nullable String);
@@ -52673,7 +52674,7 @@ package android.view.animation {
method protected android.view.animation.Animation clone() throws java.lang.CloneNotSupportedException;
method public long computeDurationHint();
method protected void ensureInterpolator();
- method @ColorInt public int getBackgroundColor();
+ method @Deprecated @ColorInt public int getBackgroundColor();
method @Deprecated public boolean getDetachWallpaper();
method public long getDuration();
method public boolean getFillAfter();
@@ -52697,7 +52698,7 @@ package android.view.animation {
method public void restrictDuration(long);
method public void scaleCurrentDuration(float);
method public void setAnimationListener(android.view.animation.Animation.AnimationListener);
- method public void setBackgroundColor(@ColorInt int);
+ method @Deprecated public void setBackgroundColor(@ColorInt int);
method @Deprecated public void setDetachWallpaper(boolean);
method public void setDuration(long);
method public void setFillAfter(boolean);
@@ -53041,6 +53042,7 @@ package android.view.contentcapture {
method @NonNull public android.view.autofill.AutofillId newAutofillId(@NonNull android.view.autofill.AutofillId, long);
method @NonNull public final android.view.ViewStructure newViewStructure(@NonNull android.view.View);
method @NonNull public final android.view.ViewStructure newVirtualViewStructure(@NonNull android.view.autofill.AutofillId, long);
+ method public final void notifySessionLifecycle(boolean);
method public final void notifyViewAppeared(@NonNull android.view.ViewStructure);
method public final void notifyViewDisappeared(@NonNull android.view.autofill.AutofillId);
method public final void notifyViewTextChanged(@NonNull android.view.autofill.AutofillId, @Nullable CharSequence);
diff --git a/api/system-current.txt b/api/system-current.txt
index 2843916c25b7..0d55b815c51f 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
@@ -8174,7 +8203,7 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean disableDataConnectivity();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean enableDataConnectivity();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean enableModemForSlot(int, boolean);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void enableVideoCalling(boolean);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void enableVideoCalling(boolean);
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getAidForAppType(int);
method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<android.service.carrier.CarrierIdentifier> getAllowedCarriers(int);
method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent);
@@ -8213,6 +8242,7 @@ package android.telephony {
method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.IccOpenLogicalChannelResponse iccOpenLogicalChannelBySlot(int, @Nullable String, int);
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String iccTransmitApduBasicChannelBySlot(int, int, int, int, int, int, @Nullable String);
method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String iccTransmitApduLogicalChannelBySlot(int, int, int, int, int, int, int, @Nullable String);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApplicationOnUicc(int);
method public boolean isDataConnectivityPossible();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isEmergencyAssistanceEnabled();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isIdle();
@@ -8220,7 +8250,7 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isPotentialEmergencyNumber(@NonNull String);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRadioOn();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRinging();
- method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isVideoCallingEnabled();
+ method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isVideoCallingEnabled();
method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isVisualVoicemailEnabled(android.telecom.PhoneAccountHandle);
method public boolean needsOtaServiceProvisioning();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean rebootRadio();
@@ -8644,6 +8674,7 @@ package android.telephony.ims {
method public android.os.Bundle getCallExtras();
method public int getCallType();
method public static int getCallTypeFromVideoState(int);
+ method public int getCallerNumberVerificationStatus();
method public int getEmergencyCallRouting();
method public int getEmergencyServiceCategories();
method @NonNull public java.util.List<java.lang.String> getEmergencyUrns();
@@ -8661,6 +8692,7 @@ package android.telephony.ims {
method public void setCallExtraBoolean(String, boolean);
method public void setCallExtraInt(String, int);
method public void setCallRestrictCause(int);
+ method public void setCallerNumberVerificationStatus(int);
method public void setEmergencyCallRouting(int);
method public void setEmergencyCallTesting(boolean);
method public void setEmergencyServiceCategories(int);
@@ -8711,6 +8743,9 @@ package android.telephony.ims {
field public static final int SERVICE_TYPE_EMERGENCY = 2; // 0x2
field public static final int SERVICE_TYPE_NONE = 0; // 0x0
field public static final int SERVICE_TYPE_NORMAL = 1; // 0x1
+ field public static final int VERIFICATION_STATUS_FAILED = 2; // 0x2
+ field public static final int VERIFICATION_STATUS_NOT_VERIFIED = 0; // 0x0
+ field public static final int VERIFICATION_STATUS_PASSED = 1; // 0x1
}
public class ImsCallSessionListener {
@@ -9485,14 +9520,18 @@ package android.telephony.ims.stub {
method public void acknowledgeSmsReport(int, int, int);
method public String getSmsFormat();
method public void onReady();
- method public final void onSendSmsResult(int, int, int, int) throws java.lang.RuntimeException;
+ method @Deprecated public final void onSendSmsResult(int, int, int, int) throws java.lang.RuntimeException;
+ method public final void onSendSmsResultError(int, int, int, int, int) throws java.lang.RuntimeException;
+ method public final void onSendSmsResultSuccess(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
field public static final int DELIVER_STATUS_ERROR_REQUEST_NOT_SUPPORTED = 4; // 0x4
field public static final int DELIVER_STATUS_OK = 1; // 0x1
+ field public static final int RESULT_NO_NETWORK_ERROR = -1; // 0xffffffff
field public static final int SEND_STATUS_ERROR = 2; // 0x2
field public static final int SEND_STATUS_ERROR_FALLBACK = 4; // 0x4
field public static final int SEND_STATUS_ERROR_RETRY = 3; // 0x3
diff --git a/api/test-current.txt b/api/test-current.txt
index 6e28f67e9a68..34312f69116a 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -34,8 +34,8 @@ package android {
package android.accessibilityservice {
- public final class AccessibilityGestureInfo implements android.os.Parcelable {
- ctor public AccessibilityGestureInfo(int, int);
+ public final class AccessibilityGestureEvent implements android.os.Parcelable {
+ ctor public AccessibilityGestureEvent(int, int);
}
}
@@ -3335,6 +3335,11 @@ package android.view {
field @android.view.ViewDebug.ExportedProperty(flagMapping={@android.view.ViewDebug.FlagToString(mask=0x1, equals=0x1, name="FAKE_HARDWARE_ACCELERATED"), @android.view.ViewDebug.FlagToString(mask=0x2, equals=0x2, name="FORCE_HARDWARE_ACCELERATED"), @android.view.ViewDebug.FlagToString(mask=0x4, equals=0x4, name="WANTS_OFFSET_NOTIFICATIONS"), @android.view.ViewDebug.FlagToString(mask=0x10, equals=0x10, name="SHOW_FOR_ALL_USERS"), @android.view.ViewDebug.FlagToString(mask=android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION, equals=android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION, name="NO_MOVE_ANIMATION"), @android.view.ViewDebug.FlagToString(mask=0x80, equals=0x80, name="COMPATIBLE_WINDOW"), @android.view.ViewDebug.FlagToString(mask=0x100, equals=0x100, name="SYSTEM_ERROR"), @android.view.ViewDebug.FlagToString(mask=0x200, equals=0x200, name="INHERIT_TRANSLUCENT_DECOR"), @android.view.ViewDebug.FlagToString(mask=0x400, equals=0x400, name="KEYGUARD"), @android.view.ViewDebug.FlagToString(mask=0x800, equals=0x800, name="DISABLE_WALLPAPER_TOUCH_EVENTS"), @android.view.ViewDebug.FlagToString(mask=0x1000, equals=0x1000, name="FORCE_STATUS_BAR_VISIBLE_TRANSPARENT"), @android.view.ViewDebug.FlagToString(mask=0x2000, equals=0x2000, name="PRESERVE_GEOMETRY"), @android.view.ViewDebug.FlagToString(mask=0x4000, equals=0x4000, name="FORCE_DECOR_VIEW_VISIBILITY"), @android.view.ViewDebug.FlagToString(mask=0x8000, equals=0x8000, name="WILL_NOT_REPLACE_ON_RELAUNCH"), @android.view.ViewDebug.FlagToString(mask=0x10000, equals=0x10000, name="LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME"), @android.view.ViewDebug.FlagToString(mask=0x20000, equals=0x20000, name="FORCE_DRAW_STATUS_BAR_BACKGROUND"), @android.view.ViewDebug.FlagToString(mask=0x40000, equals=0x40000, name="SUSTAINED_PERFORMANCE_MODE"), @android.view.ViewDebug.FlagToString(mask=0x80000, equals=0x80000, name="HIDE_NON_SYSTEM_OVERLAY_WINDOWS"), @android.view.ViewDebug.FlagToString(mask=0x100000, equals=0x100000, name="IS_ROUNDED_CORNERS_OVERLAY"), @android.view.ViewDebug.FlagToString(mask=0x400000, equals=0x400000, name="IS_SCREEN_DECOR"), @android.view.ViewDebug.FlagToString(mask=0x800000, equals=0x800000, name="STATUS_FORCE_SHOW_NAVIGATION"), @android.view.ViewDebug.FlagToString(mask=0x1000000, equals=0x1000000, name="COLOR_SPACE_AGNOSTIC")}) public int privateFlags;
}
+ public class WindowlessViewRoot {
+ ctor public WindowlessViewRoot(android.content.Context, android.view.Display, android.view.SurfaceControl);
+ method public void addView(android.view.View, android.view.WindowManager.LayoutParams);
+ }
+
}
package android.view.accessibility {
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index d4d587108a54..4c77ba402595 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -69,6 +69,7 @@ cc_library {
static_libs: [
"libandroidfw",
"libbase",
+ "libcutils",
"libutils",
"libziparchive",
],
@@ -121,6 +122,7 @@ cc_test {
static_libs: [
"libandroidfw",
"libbase",
+ "libcutils",
"libidmap2",
"liblog",
"libutils",
@@ -163,6 +165,7 @@ cc_binary {
static_libs: [
"libandroidfw",
"libbase",
+ "libcutils",
"libidmap2",
"liblog",
"libutils",
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index c9a4b3ba6368..05ff49045b17 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,58 @@ 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/state/StateManager.cpp",
+ "src/state/StateTracker.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 +122,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 +212,55 @@ 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/state/StateTracker_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 +290,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 +340,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/FieldValue.cpp b/cmds/statsd/src/FieldValue.cpp
index 1185127ab805..84a06070e431 100644
--- a/cmds/statsd/src/FieldValue.cpp
+++ b/cmds/statsd/src/FieldValue.cpp
@@ -116,28 +116,13 @@ void translateFieldMatcher(const FieldMatcher& matcher, std::vector<Matcher>* ou
}
bool isAttributionUidField(const FieldValue& value) {
- int field = value.mField.getField() & 0xff007f;
- if (field == 0x10001 && value.mValue.getType() == INT) {
- return true;
- }
- return false;
+ return isAttributionUidField(value.mField, value.mValue);
}
int32_t getUidIfExists(const FieldValue& value) {
- bool isUid = false;
// the field is uid field if the field is the uid field in attribution node or marked as
// is_uid in atoms.proto
- if (isAttributionUidField(value)) {
- isUid = true;
- } else {
- auto it = android::util::AtomsInfo::kAtomsWithUidField.find(value.mField.getTag());
- if (it != android::util::AtomsInfo::kAtomsWithUidField.end()) {
- int uidField = it->second; // uidField is the field number in proto
- isUid = value.mField.getDepth() == 0 && value.mField.getPosAtDepth(0) == uidField &&
- value.mValue.getType() == INT;
- }
- }
-
+ bool isUid = isAttributionUidField(value) || isUidField(value.mField, value.mValue);
return isUid ? value.mValue.int_value : -1;
}
@@ -153,7 +138,7 @@ bool isUidField(const Field& field, const Value& value) {
auto it = android::util::AtomsInfo::kAtomsWithUidField.find(field.getTag());
if (it != android::util::AtomsInfo::kAtomsWithUidField.end()) {
- int uidField = it->second;
+ int uidField = it->second; // uidField is the field number in proto
return field.getDepth() == 0 && field.getPosAtDepth(0) == uidField &&
value.getType() == INT;
}
diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp
index af8b3af6ea61..5e156bb26caa 100644
--- a/cmds/statsd/src/HashableDimensionKey.cpp
+++ b/cmds/statsd/src/HashableDimensionKey.cpp
@@ -59,6 +59,16 @@ android::hash_t hashDimension(const HashableDimensionKey& value) {
return JenkinsHashWhiten(hash);
}
+bool filterValues(const Matcher& matcherField, const vector<FieldValue>& values, Value* output) {
+ for (const auto& value : values) {
+ if (value.mField.matches(matcherField)) {
+ (*output) = value.mValue;
+ return true;
+ }
+ }
+ return false;
+}
+
bool filterValues(const vector<Matcher>& matcherFields, const vector<FieldValue>& values,
HashableDimensionKey* output) {
size_t num_matches = 0;
diff --git a/cmds/statsd/src/HashableDimensionKey.h b/cmds/statsd/src/HashableDimensionKey.h
index 6f4941f717ee..a12385057585 100644
--- a/cmds/statsd/src/HashableDimensionKey.h
+++ b/cmds/statsd/src/HashableDimensionKey.h
@@ -120,6 +120,13 @@ class MetricDimensionKey {
android::hash_t hashDimension(const HashableDimensionKey& key);
/**
+ * Returns true if a FieldValue field matches the matcher field.
+ * The value of the FieldValue is output.
+ */
+bool filterValues(const Matcher& matcherField, const std::vector<FieldValue>& values,
+ Value* output);
+
+/**
* Creating HashableDimensionKeys from FieldValues using matcher.
*
* This function may make modifications to the Field if the matcher has Position=FIRST,LAST or ALL
@@ -169,4 +176,4 @@ struct hash<MetricDimensionKey> {
return android::JenkinsHashWhiten(hash);
}
};
-} // namespace std \ No newline at end of file
+} // namespace std
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index fe3c3d5da6df..b71a86b87d49 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -335,10 +335,12 @@ message Atom {
UpdateEngineUpdateAttemptReported update_engine_update_attempt_reported = 225;
UpdateEngineSuccessfulUpdateReported update_engine_successful_update_reported = 226;
CameraActionEvent camera_action_event = 227;
+ AppCompatibilityChangeReported app_compatibility_change_reported =
+ 228 [(allow_from_any_uid) = true];
}
// Pulled events will start at field 10000.
- // Next: 10064
+ // Next: 10065
oneof pulled {
WifiBytesTransfer wifi_bytes_transfer = 10000;
WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001;
@@ -404,6 +406,7 @@ message Atom {
ProcessSystemIonHeapSize process_system_ion_heap_size = 10061;
SurfaceflingerStatsGlobalInfo surfaceflinger_stats_global_info = 10062;
SurfaceflingerStatsLayerInfo surfaceflinger_stats_layer_info = 10063;
+ ProcessMemorySnapshot process_memory_snapshot = 10064;
}
// DO NOT USE field numbers above 100,000 in AOSP.
@@ -2618,12 +2621,14 @@ message SettingsUIChanged {
message TouchEventReported {
/**
* The fields latency_{min|max|mean|stdev} represent minimum, maximum, mean,
- * and the standard deviation of latency between the kernel and framework
- * for touchscreen events. The units are microseconds.
+ * and the standard deviation of the time spent processing touchscreen events
+ * in the kernel and inputflinger. The units are microseconds.
*
- * The number is measured as the difference between the time at which
- * the input event was received in the evdev driver,
- * and the time at which the input event was received in EventHub.
+ * On supported devices, the starting point is taken during the hard interrupt inside the
+ * kernel touch driver. On all other devices, the starting point is taken inside
+ * the kernel's input event subsystem upon receipt of the input event.
+ * The ending point is taken inside InputDispatcher, just after the input event
+ * is sent to the app.
*/
// Minimum value
optional float latency_min_micros = 1;
@@ -3065,9 +3070,9 @@ message PictureInPictureStateChanged {
* services/core/java/com/android/server/wm/Session.java
*/
message OverlayStateChanged {
- optional int32 uid = 1 [(is_uid) = true];
+ optional int32 uid = 1 [(state_field_option).option = PRIMARY, (is_uid) = true];
- optional string package_name = 2;
+ optional string package_name = 2 [(state_field_option).option = PRIMARY];
optional bool using_alert_window = 3;
@@ -3075,7 +3080,7 @@ message OverlayStateChanged {
ENTERED = 1;
EXITED = 2;
}
- optional State state = 4;
+ optional State state = 4 [(state_field_option).option = EXCLUSIVE];
}
/*
@@ -3968,7 +3973,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];
}
/**
@@ -4104,6 +4109,46 @@ message ProcessMemoryHighWaterMark {
}
/*
+ * Logs the memory stats for a process.
+ *
+ * Pulled from StatsCompanionService for all managed processes (from ActivityManagerService)
+ * and for selected native processes.
+ */
+message ProcessMemorySnapshot {
+ // The uid if available. -1 means not available.
+ optional int32 uid = 1 [(is_uid) = true];
+
+ // The process name.
+ // Usually package name or process cmdline.
+ // Provided by ActivityManagerService or read from /proc/PID/cmdline.
+ optional string process_name = 2;
+
+ // The pid of the process.
+ // Allows to disambiguate instances of the process.
+ optional int32 pid = 3;
+
+ // The current OOM score adjustment value.
+ // Read from ProcessRecord for managed processes.
+ // Placeholder -1001 (OOM_SCORE_ADJ_MIN - 1, outside of allowed range) for native ones.
+ optional int32 oom_score_adj = 4;
+
+ // The current RSS of the process.
+ // VmRSS from /proc/pid/status.
+ optional int32 rss_in_kilobytes = 5;
+
+ // The current anon RSS of the process.
+ // RssAnon from /proc/pid/status.
+ optional int32 anon_rss_in_kilobytes = 6;
+
+ // The current swap size of the process.
+ // VmSwap from /proc/pid/status.
+ optional int32 swap_in_kilobytes = 7;
+
+ // The sum of rss_in_kilobytes and swap_in_kilobytes.
+ optional int32 anon_rss_and_swap_in_kilobytes = 8;
+}
+
+/*
* Elapsed real time from SystemClock.
*/
message SystemElapsedRealtime {
@@ -7170,7 +7215,6 @@ message FrameTimingHistogram {
repeated int64 frame_counts = 2;
}
-
/**
* Information about camera facing and API level usage.
* Logged from:
@@ -7195,3 +7239,39 @@ message CameraActionEvent {
}
optional Facing facing = 4;
}
+
+/**
+ * Logs when a compatibility change is affecting an app.
+ *
+ * Logged from:
+ * frameworks/base/core/java/android/app/AppCompatCallbacks.java and
+ * frameworks/base/services/core/java/com/android/server/compat/PlatformCompat.java
+ */
+message AppCompatibilityChangeReported {
+ // The UID of the app being affected by the compatibilty change.
+ optional int32 uid = 1 [(is_uid) = true];
+
+ // The ID of the change affecting the app.
+ optional int64 change_id = 2;
+
+ enum State {
+ UNKNOWN_STATE = 0;
+ ENABLED = 1;
+ DISABLED = 2;
+ LOGGED = 3;
+ }
+
+ // The state of the change - if logged from gating whether it was enabled or disabled, or just
+ // logged otherwise.
+ optional State state = 3;
+
+ enum Source {
+ UNKNOWN_SOURCE = 0;
+ APP_PROCESS = 1;
+ SYSTEM_SERVER = 2;
+ }
+
+ // Where it was logged from.
+ optional Source source = 4;
+
+}
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 475f18a9b0b8..f69e2d09ad23 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -153,6 +153,9 @@ std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = {
{.additiveFields = {3},
.puller =
new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_HIGH_WATER_MARK)}},
+ // process_memory_snapshot
+ {android::util::PROCESS_MEMORY_SNAPSHOT,
+ {.puller = new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_SNAPSHOT)}},
// system_ion_heap_size
{android::util::SYSTEM_ION_HEAP_SIZE,
{.puller = new StatsCompanionServicePuller(android::util::SYSTEM_ION_HEAP_SIZE)}},
diff --git a/cmds/statsd/src/matchers/matcher_util.cpp b/cmds/statsd/src/matchers/matcher_util.cpp
index 10ac4a182f87..476fae37899d 100644
--- a/cmds/statsd/src/matchers/matcher_util.cpp
+++ b/cmds/statsd/src/matchers/matcher_util.cpp
@@ -358,9 +358,10 @@ bool matchesSimple(const UidMap& uidMap, const FieldValueMatcher& matcher,
bool matchesSimple(const UidMap& uidMap, const SimpleAtomMatcher& simpleMatcher,
const LogEvent& event) {
- if (simpleMatcher.field_value_matcher_size() <= 0) {
- return event.GetTagId() == simpleMatcher.atom_id();
+ if (event.GetTagId() != simpleMatcher.atom_id()) {
+ return false;
}
+
for (const auto& matcher : simpleMatcher.field_value_matcher()) {
if (!matchesSimple(uidMap, matcher, event.getValues(), 0, event.getValues().size(), 0)) {
return false;
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 94f833b20814..fdbdc83fb66e 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -83,9 +83,9 @@ public:
mCurrentBucketStartTimeNs(timeBaseNs),
mCurrentBucketNum(0),
mCondition(initialCondition(conditionIndex)),
+ mConditionTrackerIndex(conditionIndex),
mConditionSliced(false),
mWizard(wizard),
- mConditionTrackerIndex(conditionIndex),
mContainANYPositionInDimensionsInWhat(false),
mSliceByPositionALL(false),
mHasLinksToAllConditionDimensionsInTracker(false),
@@ -167,11 +167,6 @@ public:
return clearPastBucketsLocked(dumpTimeNs);
}
- void dumpStates(FILE* out, bool verbose) const {
- std::lock_guard<std::mutex> lock(mMutex);
- dumpStatesLocked(out, verbose);
- }
-
// Returns the memory in bytes currently used to store this metric's data. Does not change
// state.
size_t byteSize() const {
@@ -179,34 +174,9 @@ public:
return byteSizeLocked();
}
- /* If alert is valid, adds an AnomalyTracker and returns it. If invalid, returns nullptr. */
- virtual sp<AnomalyTracker> addAnomalyTracker(const Alert &alert,
- const sp<AlarmMonitor>& anomalyAlarmMonitor) {
- std::lock_guard<std::mutex> lock(mMutex);
- sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, mConfigKey);
- if (anomalyTracker != nullptr) {
- mAnomalyTrackers.push_back(anomalyTracker);
- }
- return anomalyTracker;
- }
-
- int64_t getBuckeSizeInNs() const {
- std::lock_guard<std::mutex> lock(mMutex);
- return mBucketSizeNs;
- }
-
- // Only needed for unit-testing to override guardrail.
- void setBucketSize(int64_t bucketSize) {
- mBucketSizeNs = bucketSize;
- }
-
- inline const int64_t& getMetricId() const {
- return mMetricId;
- }
-
- void loadActiveMetric(const ActiveMetric& activeMetric, int64_t currentTimeNs) {
+ void dumpStates(FILE* out, bool verbose) const {
std::lock_guard<std::mutex> lock(mMutex);
- loadActiveMetricLocked(activeMetric, currentTimeNs);
+ dumpStatesLocked(out, verbose);
}
// Let MetricProducer drop in-memory data to save memory.
@@ -218,9 +188,14 @@ public:
dropDataLocked(dropTimeNs);
}
- // For test only.
- inline int64_t getCurrentBucketNum() const {
- return mCurrentBucketNum;
+ void prepareFirstBucket() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ prepareFirstBucketLocked();
+ }
+
+ void loadActiveMetric(const ActiveMetric& activeMetric, int64_t currentTimeNs) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ loadActiveMetricLocked(activeMetric, currentTimeNs);
}
void activate(int activationTrackerIndex, int64_t elapsedTimestampNs) {
@@ -238,44 +213,41 @@ public:
return isActiveLocked();
}
+ void flushIfExpire(int64_t elapsedTimestampNs);
+
void addActivation(int activationTrackerIndex, const ActivationType& activationType,
int64_t ttl_seconds, int deactivationTrackerIndex = -1);
- void prepareFirstBucket() {
- std::lock_guard<std::mutex> lock(mMutex);
- prepareFirstBucketLocked();
- }
-
- void flushIfExpire(int64_t elapsedTimestampNs);
-
void writeActiveMetricToProtoOutputStream(
int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto);
-protected:
- virtual void onConditionChangedLocked(const bool condition, const int64_t eventTime) = 0;
- virtual void onSlicedConditionMayChangeLocked(bool overallCondition,
- const int64_t eventTime) = 0;
- virtual void onDumpReportLocked(const int64_t dumpTimeNs,
- const bool include_current_partial_bucket,
- const bool erase_data,
- const DumpLatency dumpLatency,
- std::set<string> *str_set,
- android::util::ProtoOutputStream* protoOutput) = 0;
- virtual void clearPastBucketsLocked(const int64_t dumpTimeNs) = 0;
- virtual size_t byteSizeLocked() const = 0;
- virtual void dumpStatesLocked(FILE* out, bool verbose) const = 0;
- bool evaluateActiveStateLocked(int64_t elapsedTimestampNs);
-
- void activateLocked(int activationTrackerIndex, int64_t elapsedTimestampNs);
- void cancelEventActivationLocked(int deactivationTrackerIndex);
+ // Start: getters/setters
+ inline const int64_t& getMetricId() const {
+ return mMetricId;
+ }
- inline bool isActiveLocked() const {
- return mIsActive;
+ // For test only.
+ inline int64_t getCurrentBucketNum() const {
+ return mCurrentBucketNum;
}
- void loadActiveMetricLocked(const ActiveMetric& activeMetric, int64_t currentTimeNs);
+ int64_t getBucketSizeInNs() const {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mBucketSizeNs;
+ }
- virtual void prepareFirstBucketLocked() {};
+ /* If alert is valid, adds an AnomalyTracker and returns it. If invalid, returns nullptr. */
+ virtual sp<AnomalyTracker> addAnomalyTracker(const Alert &alert,
+ const sp<AlarmMonitor>& anomalyAlarmMonitor) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, mConfigKey);
+ if (anomalyTracker != nullptr) {
+ mAnomalyTrackers.push_back(anomalyTracker);
+ }
+ return anomalyTracker;
+ }
+ // End: getters/setters
+protected:
/**
* Flushes the current bucket if the eventTime is after the current bucket's end time. This will
also flush the current partial bucket in memory.
@@ -283,14 +255,6 @@ protected:
virtual void flushIfNeededLocked(const int64_t& eventTime){};
/**
- * Flushes all the data including the current partial bucket.
- */
- virtual void flushLocked(const int64_t& eventTimeNs) {
- flushIfNeededLocked(eventTimeNs);
- flushCurrentBucketLocked(eventTimeNs, eventTimeNs);
- };
-
- /**
* For metrics that aggregate (ie, every metric producer except for EventMetricProducer),
* we need to be able to flush the current buckets on demand (ie, end the current bucket and
* start new bucket). If this function is called when eventTimeNs is greater than the current
@@ -303,12 +267,66 @@ protected:
virtual void flushCurrentBucketLocked(const int64_t& eventTimeNs,
const int64_t& nextBucketStartTimeNs) {};
+ /**
+ * Flushes all the data including the current partial bucket.
+ */
+ virtual void flushLocked(const int64_t& eventTimeNs) {
+ flushIfNeededLocked(eventTimeNs);
+ flushCurrentBucketLocked(eventTimeNs, eventTimeNs);
+ };
+
+ /*
+ * Individual metrics can implement their own business logic here. All pre-processing is done.
+ *
+ * [matcherIndex]: the index of the matcher which matched this event. This is interesting to
+ * DurationMetric, because it has start/stop/stop_all 3 matchers.
+ * [eventKey]: the extracted dimension key for the final output. if the metric doesn't have
+ * dimensions, it will be DEFAULT_DIMENSION_KEY
+ * [conditionKey]: the keys of conditions which should be used to query the condition for this
+ * target event (from MetricConditionLink). This is passed to individual metrics
+ * because DurationMetric needs it to be cached.
+ * [condition]: whether condition is met. If condition is sliced, this is the result coming from
+ * query with ConditionWizard; If condition is not sliced, this is the
+ * nonSlicedCondition.
+ * [event]: the log event, just in case the metric needs its data, e.g., EventMetric.
+ */
+ virtual void onMatchedLogEventInternalLocked(
+ const size_t matcherIndex, const MetricDimensionKey& eventKey,
+ const ConditionKey& conditionKey, bool condition,
+ const LogEvent& event) = 0;
+
+ // Consume the parsed stats log entry that already matched the "what" of the metric.
+ virtual void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event);
+ virtual void onConditionChangedLocked(const bool condition, const int64_t eventTime) = 0;
+ virtual void onSlicedConditionMayChangeLocked(bool overallCondition,
+ const int64_t eventTime) = 0;
+ virtual void onDumpReportLocked(const int64_t dumpTimeNs,
+ const bool include_current_partial_bucket,
+ const bool erase_data,
+ const DumpLatency dumpLatency,
+ std::set<string> *str_set,
+ android::util::ProtoOutputStream* protoOutput) = 0;
+ virtual void clearPastBucketsLocked(const int64_t dumpTimeNs) = 0;
+ virtual size_t byteSizeLocked() const = 0;
+ virtual void dumpStatesLocked(FILE* out, bool verbose) const = 0;
+ virtual void dropDataLocked(const int64_t dropTimeNs) = 0;
+ virtual void prepareFirstBucketLocked() {};
+ void loadActiveMetricLocked(const ActiveMetric& activeMetric, int64_t currentTimeNs);
+ void activateLocked(int activationTrackerIndex, int64_t elapsedTimestampNs);
+ void cancelEventActivationLocked(int deactivationTrackerIndex);
+
+ bool evaluateActiveStateLocked(int64_t elapsedTimestampNs);
+
virtual void onActiveStateChangedLocked(const int64_t& eventTimeNs) {
if (!mIsActive) {
flushLocked(eventTimeNs);
}
}
+ inline bool isActiveLocked() const {
+ return mIsActive;
+ }
+
// Convenience to compute the current bucket's end time, which is always aligned with the
// start time of the metric.
int64_t getCurrentBucketEndTimeNs() const {
@@ -319,8 +337,6 @@ protected:
return (endNs - mTimeBaseNs) / mBucketSizeNs - 1;
}
- virtual void dropDataLocked(const int64_t dropTimeNs) = 0;
-
const int64_t mMetricId;
const ConfigKey mConfigKey;
@@ -341,17 +357,18 @@ protected:
ConditionState mCondition;
+ int mConditionTrackerIndex;
+
bool mConditionSliced;
sp<ConditionWizard> mWizard;
- int mConditionTrackerIndex;
-
- vector<Matcher> mDimensionsInWhat; // The dimensions_in_what defined in statsd_config
-
bool mContainANYPositionInDimensionsInWhat;
+
bool mSliceByPositionALL;
+ vector<Matcher> mDimensionsInWhat; // The dimensions_in_what defined in statsd_config
+
// True iff the metric to condition links cover all dimension fields in the condition tracker.
// This field is always false for combinational condition trackers.
bool mHasLinksToAllConditionDimensionsInTracker;
@@ -360,29 +377,6 @@ protected:
std::vector<sp<AnomalyTracker>> mAnomalyTrackers;
- /*
- * Individual metrics can implement their own business logic here. All pre-processing is done.
- *
- * [matcherIndex]: the index of the matcher which matched this event. This is interesting to
- * DurationMetric, because it has start/stop/stop_all 3 matchers.
- * [eventKey]: the extracted dimension key for the final output. if the metric doesn't have
- * dimensions, it will be DEFAULT_DIMENSION_KEY
- * [conditionKey]: the keys of conditions which should be used to query the condition for this
- * target event (from MetricConditionLink). This is passed to individual metrics
- * because DurationMetric needs it to be cached.
- * [condition]: whether condition is met. If condition is sliced, this is the result coming from
- * query with ConditionWizard; If condition is not sliced, this is the
- * nonSlicedCondition.
- * [event]: the log event, just in case the metric needs its data, e.g., EventMetric.
- */
- virtual void onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const MetricDimensionKey& eventKey,
- const ConditionKey& conditionKey, bool condition,
- const LogEvent& event) = 0;
-
- // Consume the parsed stats log entry that already matched the "what" of the metric.
- virtual void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event);
-
mutable std::mutex mMutex;
struct Activation {
@@ -397,6 +391,7 @@ protected:
ActivationState state;
const ActivationType activationType;
};
+
// When the metric producer has multiple activations, these activations are ORed to determine
// whether the metric producer is ready to generate metrics.
std::unordered_map<int, std::shared_ptr<Activation>> mEventActivationMap;
diff --git a/cmds/statsd/src/state/StateListener.h b/cmds/statsd/src/state/StateListener.h
new file mode 100644
index 000000000000..a31690a102ed
--- /dev/null
+++ b/cmds/statsd/src/state/StateListener.h
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include <utils/RefBase.h>
+
+#include "HashableDimensionKey.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class StateListener : public virtual RefBase {
+public:
+ StateListener(){};
+
+ virtual ~StateListener(){};
+
+ /**
+ * Interface for handling a state change.
+ *
+ * The old and new state values map to the original state values.
+ * StateTrackers only track the original state values and are unaware
+ * of higher-level state groups. MetricProducers hold information on
+ * state groups and are responsible for mapping original state values to
+ * the correct state group.
+ *
+ * [atomId]: The id of the state atom
+ * [primaryKey]: The primary field values of the state atom
+ * [oldState]: Previous state value before state change
+ * [newState]: Current state value after state change
+ */
+ virtual void onStateChanged(int atomId, const HashableDimensionKey& primaryKey, int oldState,
+ int newState) = 0;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/state/StateManager.cpp b/cmds/statsd/src/state/StateManager.cpp
new file mode 100644
index 000000000000..a3059c5b52ac
--- /dev/null
+++ b/cmds/statsd/src/state/StateManager.cpp
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+#define DEBUG false // STOPSHIP if true
+#include "Log.h"
+
+#include "StateManager.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+StateManager& StateManager::getInstance() {
+ static StateManager sStateManager;
+ return sStateManager;
+}
+
+void StateManager::onLogEvent(const LogEvent& event) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (mStateTrackers.find(event.GetTagId()) != mStateTrackers.end()) {
+ mStateTrackers[event.GetTagId()]->onLogEvent(event);
+ }
+}
+
+bool StateManager::registerListener(int stateAtomId, wp<StateListener> listener) {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ // Check if state tracker already exists
+ if (mStateTrackers.find(stateAtomId) == mStateTrackers.end()) {
+ // Create a new state tracker iff atom is a state atom
+ auto it = android::util::AtomsInfo::kStateAtomsFieldOptions.find(stateAtomId);
+ if (it != android::util::AtomsInfo::kStateAtomsFieldOptions.end()) {
+ mStateTrackers[stateAtomId] = new StateTracker(stateAtomId, it->second);
+ } else {
+ ALOGE("StateManager cannot register listener, Atom %d is not a state atom",
+ stateAtomId);
+ return false;
+ }
+ }
+ mStateTrackers[stateAtomId]->registerListener(listener);
+ return true;
+}
+
+void StateManager::unregisterListener(int stateAtomId, wp<StateListener> listener) {
+ std::unique_lock<std::mutex> lock(mMutex);
+
+ // Hold the sp<> until the lock is released so that ~StateTracker() is
+ // not called while the lock is held.
+ sp<StateTracker> toRemove;
+
+ // Unregister listener from correct StateTracker
+ auto it = mStateTrackers.find(stateAtomId);
+ if (it != mStateTrackers.end()) {
+ it->second->unregisterListener(listener);
+
+ // Remove the StateTracker if it has no listeners
+ if (it->second->getListenersCount() == 0) {
+ toRemove = it->second;
+ mStateTrackers.erase(it);
+ }
+ } else {
+ ALOGE("StateManager cannot unregister listener, StateTracker for atom %d does not exist",
+ stateAtomId);
+ }
+ lock.unlock();
+}
+
+int StateManager::getState(int stateAtomId, const HashableDimensionKey& key) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (mStateTrackers.find(stateAtomId) != mStateTrackers.end()) {
+ return mStateTrackers[stateAtomId]->getState(key);
+ }
+
+ return StateTracker::kStateUnknown;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/state/StateManager.h b/cmds/statsd/src/state/StateManager.h
new file mode 100644
index 000000000000..ce60f1482be7
--- /dev/null
+++ b/cmds/statsd/src/state/StateManager.h
@@ -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.
+ */
+#pragma once
+
+//#include <utils/Log.h>
+#include <utils/RefBase.h>
+#include "HashableDimensionKey.h"
+
+#include "state/StateListener.h"
+#include "state/StateTracker.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class StateManager : public virtual RefBase {
+public:
+ StateManager(){};
+
+ ~StateManager(){};
+
+ // Returns a pointer to the single, shared StateManager object.
+ static StateManager& getInstance();
+
+ // Notifies the correct StateTracker of an event.
+ void onLogEvent(const LogEvent& event);
+
+ // Returns true if stateAtomId is the id of a state atom and notifies the
+ // correct StateTracker to register the listener. If the correct
+ // StateTracker does not exist, a new StateTracker is created.
+ bool registerListener(int stateAtomId, wp<StateListener> listener);
+
+ // Notifies the correct StateTracker to unregister a listener
+ // and removes the tracker if it no longer has any listeners.
+ void unregisterListener(int stateAtomId, wp<StateListener> listener);
+
+ // Queries the correct StateTracker for the state that is mapped to the given
+ // query key.
+ // If the StateTracker doesn't exist, returns StateTracker::kStateUnknown.
+ int getState(int stateAtomId, const HashableDimensionKey& queryKey);
+
+ inline int getStateTrackersCount() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mStateTrackers.size();
+ }
+
+ inline int getListenersCount(int stateAtomId) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (mStateTrackers.find(stateAtomId) != mStateTrackers.end()) {
+ return mStateTrackers[stateAtomId]->getListenersCount();
+ }
+ return -1;
+ }
+
+private:
+ mutable std::mutex mMutex;
+
+ // Maps state atom ids to StateTrackers
+ std::unordered_map<int, sp<StateTracker>> mStateTrackers;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/state/StateTracker.cpp b/cmds/statsd/src/state/StateTracker.cpp
new file mode 100644
index 000000000000..5a91950b9f8b
--- /dev/null
+++ b/cmds/statsd/src/state/StateTracker.cpp
@@ -0,0 +1,159 @@
+/*
+ * 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.
+ */
+
+#define DEBUG true // STOPSHIP if true
+#include "Log.h"
+
+#include "stats_util.h"
+
+#include "StateTracker.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+StateTracker::StateTracker(const int atomId,
+ const util::StateAtomFieldOptions& stateAtomInfo)
+ : mAtomId(atomId),
+ mStateField(getSimpleMatcher(atomId, stateAtomInfo.exclusiveField)) {
+ // create matcher for each primary field
+ // TODO(tsaichristine): handle when primary field is first uid in chain
+ for (const auto& primary : stateAtomInfo.primaryFields) {
+ Matcher matcher = getSimpleMatcher(atomId, primary);
+ mPrimaryFields.push_back(matcher);
+ }
+
+ // TODO(tsaichristine): set default state, reset state, and nesting
+}
+
+void StateTracker::onLogEvent(const LogEvent& event) {
+ // parse event for primary field values i.e. primary key
+ HashableDimensionKey primaryKey;
+ if (mPrimaryFields.size() > 0) {
+ if (!filterValues(mPrimaryFields, event.getValues(), &primaryKey) ||
+ primaryKey.getValues().size() != mPrimaryFields.size()) {
+ ALOGE("StateTracker error extracting primary key from log event.");
+ handleReset();
+ return;
+ }
+ } else {
+ // atom has no primary fields
+ primaryKey = DEFAULT_DIMENSION_KEY;
+ }
+
+ // parse event for state value
+ Value state;
+ int32_t stateValue;
+ if (!filterValues(mStateField, event.getValues(), &state) || state.getType() != INT) {
+ ALOGE("StateTracker error extracting state from log event. Type: %d", state.getType());
+ handlePartialReset(primaryKey);
+ return;
+ }
+ stateValue = state.int_value;
+
+ if (stateValue == mResetState) {
+ VLOG("StateTracker Reset state: %s", state.toString().c_str());
+ handleReset();
+ }
+
+ // track and update state
+ int32_t oldState = 0;
+ int32_t newState = 0;
+ updateState(primaryKey, stateValue, &oldState, &newState);
+
+ // notify all listeners if state has changed
+ if (oldState != newState) {
+ VLOG("StateTracker updated state");
+ for (auto listener : mListeners) {
+ auto sListener = listener.promote(); // safe access to wp<>
+ if (sListener != nullptr) {
+ sListener->onStateChanged(mAtomId, primaryKey, oldState, newState);
+ }
+ }
+ } else {
+ VLOG("StateTracker NO updated state");
+ }
+}
+
+void StateTracker::registerListener(wp<StateListener> listener) {
+ mListeners.insert(listener);
+}
+
+void StateTracker::unregisterListener(wp<StateListener> listener) {
+ mListeners.erase(listener);
+}
+
+int StateTracker::getState(const HashableDimensionKey& queryKey) const {
+ if (queryKey.getValues().size() == mPrimaryFields.size()) {
+ auto it = mStateMap.find(queryKey);
+ if (it != mStateMap.end()) {
+ return it->second.state;
+ }
+ } else if (queryKey.getValues().size() > mPrimaryFields.size()) {
+ ALOGE("StateTracker query key size > primary key size is illegal");
+ } else {
+ ALOGE("StateTracker query key size < primary key size is not supported");
+ }
+ return mDefaultState;
+}
+
+void StateTracker::handleReset() {
+ VLOG("StateTracker handle reset");
+ for (const auto pair : mStateMap) {
+ for (auto l : mListeners) {
+ auto sl = l.promote();
+ if (sl != nullptr) {
+ sl->onStateChanged(mAtomId, pair.first, pair.second.state, mDefaultState);
+ }
+ }
+ }
+ mStateMap.clear();
+}
+
+void StateTracker::handlePartialReset(const HashableDimensionKey& primaryKey) {
+ VLOG("StateTracker handle partial reset");
+ if (mStateMap.find(primaryKey) != mStateMap.end()) {
+ mStateMap.erase(primaryKey);
+ }
+}
+
+void StateTracker::updateState(const HashableDimensionKey& primaryKey, const int32_t eventState,
+ int32_t* oldState, int32_t* newState) {
+ // get old state (either current state in map or default state)
+ auto it = mStateMap.find(primaryKey);
+ if (it != mStateMap.end()) {
+ *oldState = it->second.state;
+ } else {
+ *oldState = mDefaultState;
+ }
+
+ // update state map
+ if (eventState == mDefaultState) {
+ // remove (key, state) pair if state returns to default state
+ VLOG("\t StateTracker changed to default state")
+ mStateMap.erase(primaryKey);
+ } else {
+ mStateMap[primaryKey].state = eventState;
+ mStateMap[primaryKey].count = 1;
+ }
+ *newState = eventState;
+
+ // TODO(tsaichristine): support atoms with nested counting
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/state/StateTracker.h b/cmds/statsd/src/state/StateTracker.h
new file mode 100644
index 000000000000..f22706c8418d
--- /dev/null
+++ b/cmds/statsd/src/state/StateTracker.h
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include <statslog.h>
+#include <utils/RefBase.h>
+#include "HashableDimensionKey.h"
+#include "logd/LogEvent.h"
+
+#include "state/StateListener.h"
+
+#include <unordered_map>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class StateTracker : public virtual RefBase {
+public:
+ StateTracker(const int atomId, const util::StateAtomFieldOptions& stateAtomInfo);
+
+ virtual ~StateTracker(){};
+
+ // Updates state map and notifies all listeners if a state change occurs.
+ // Checks if a state change has occurred by getting the state value from
+ // the log event and comparing the old and new states.
+ void onLogEvent(const LogEvent& event);
+
+ // Adds new listeners to set of StateListeners. If a listener is already
+ // registered, it is ignored.
+ void registerListener(wp<StateListener> listener);
+
+ void unregisterListener(wp<StateListener> listener);
+
+ // Returns the state value mapped to the given query key.
+ // If the key isn't mapped to a state or the key size doesn't match the
+ // primary key size, the default state is returned.
+ int getState(const HashableDimensionKey& queryKey) const;
+
+ inline int getListenersCount() const {
+ return mListeners.size();
+ }
+
+ const static int kStateUnknown = -1;
+
+private:
+ struct StateValueInfo {
+ int32_t state; // state value
+ int count; // nested count (only used for binary states)
+ };
+
+ const int32_t mAtomId; // id of the state atom being tracked
+
+ Matcher mStateField; // matches the atom's exclusive state field
+
+ std::vector<Matcher> mPrimaryFields; // matches the atom's primary fields
+
+ int32_t mDefaultState = kStateUnknown;
+
+ int32_t mResetState;
+
+ // Maps primary key to state value info
+ std::unordered_map<HashableDimensionKey, StateValueInfo> mStateMap;
+
+ // Set of all StateListeners (objects listening for state changes)
+ std::set<wp<StateListener>> mListeners;
+
+ // Reset all state values in map to default state
+ void handleReset();
+
+ // Reset only the state value mapped to primary key to default state
+ void handlePartialReset(const HashableDimensionKey& primaryKey);
+
+ // Update the StateMap based on the received state value.
+ // Store the old and new states.
+ void updateState(const HashableDimensionKey& primaryKey, const int32_t eventState,
+ int32_t* oldState, int32_t* newState);
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/tests/LogEntryMatcher_test.cpp b/cmds/statsd/tests/LogEntryMatcher_test.cpp
index 70f0f6f75a59..441d3c896467 100644
--- a/cmds/statsd/tests/LogEntryMatcher_test.cpp
+++ b/cmds/statsd/tests/LogEntryMatcher_test.cpp
@@ -330,6 +330,7 @@ TEST(AtomMatcherTest, TestUidFieldMatcher) {
EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
// Tag found in kAtomsWithUidField and has matching uid
+ simpleMatcher->set_atom_id(TAG_ID_2);
EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2));
// Tag found in kAtomsWithUidField but has non-matching uid
diff --git a/cmds/statsd/tests/state/StateTracker_test.cpp b/cmds/statsd/tests/state/StateTracker_test.cpp
new file mode 100644
index 000000000000..c89ffea85709
--- /dev/null
+++ b/cmds/statsd/tests/state/StateTracker_test.cpp
@@ -0,0 +1,356 @@
+/*
+ * 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 <gtest/gtest.h>
+#include "state/StateManager.h"
+#include "state/StateTracker.h"
+#include "state/StateListener.h"
+
+#include "tests/statsd_test_util.h"
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+/**
+ * Mock StateListener class for testing.
+ * Stores primary key and state pairs.
+ */
+class TestStateListener : public virtual StateListener {
+public:
+ TestStateListener(){};
+
+ virtual ~TestStateListener(){};
+
+ struct Update {
+ Update(const HashableDimensionKey& key, int state) : mKey(key), mState(state){};
+ HashableDimensionKey mKey;
+ int mState;
+ };
+
+ std::vector<Update> updates;
+
+ void onStateChanged(int stateAtomId, const HashableDimensionKey& primaryKey, int oldState,
+ int newState) {
+ updates.emplace_back(primaryKey, newState);
+ }
+};
+
+// START: build event functions.
+// State with no primary fields - ScreenStateChanged
+std::shared_ptr<LogEvent> buildScreenEvent(int state) {
+ std::shared_ptr<LogEvent> event =
+ std::make_shared<LogEvent>(android::util::SCREEN_STATE_CHANGED, 1000 /*timestamp*/);
+ event->write((int32_t)state);
+ event->init();
+ return event;
+}
+
+// State with one primary field - UidProcessStateChanged
+std::shared_ptr<LogEvent> buildUidProcessEvent(int uid, int state) {
+ std::shared_ptr<LogEvent> event =
+ std::make_shared<LogEvent>(android::util::UID_PROCESS_STATE_CHANGED, 1000 /*timestamp*/);
+ event->write((int32_t)uid);
+ event->write((int32_t)state);
+ event->init();
+ return event;
+}
+
+// State with multiple primary fields - OverlayStateChanged
+std::shared_ptr<LogEvent> buildOverlayEvent(int uid, const std::string& packageName, int state) {
+ std::shared_ptr<LogEvent> event =
+ std::make_shared<LogEvent>(android::util::OVERLAY_STATE_CHANGED, 1000 /*timestamp*/);
+ event->write((int32_t)uid);
+ event->write(packageName);
+ event->write(true); // using_alert_window
+ event->write((int32_t)state);
+ event->init();
+ return event;
+}
+
+std::shared_ptr<LogEvent> buildIncorrectOverlayEvent(int uid, const std::string& packageName, int state) {
+ std::shared_ptr<LogEvent> event =
+ std::make_shared<LogEvent>(android::util::OVERLAY_STATE_CHANGED, 1000 /*timestamp*/);
+ event->write((int32_t)uid);
+ event->write(packageName);
+ event->write((int32_t)state);
+ event->init();
+ return event;
+}
+// END: build event functions.
+
+// START: get primary key functions
+void getUidProcessKey(int uid, HashableDimensionKey* key) {
+ int pos1[] = {1, 0, 0};
+ Field field1(27 /* atom id */, pos1, 0 /* depth */);
+ Value value1((int32_t)uid);
+
+ key->addValue(FieldValue(field1, value1));
+}
+
+void getOverlayKey(int uid, string packageName, HashableDimensionKey* key) {
+ int pos1[] = {1, 0, 0};
+ int pos2[] = {2, 0, 0};
+
+ Field field1(59 /* atom id */, pos1, 0 /* depth */);
+ Field field2(59 /* atom id */, pos2, 0 /* depth */);
+
+ Value value1((int32_t)uid);
+ Value value2(packageName);
+
+ key->addValue(FieldValue(field1, value1));
+ key->addValue(FieldValue(field2, value2));
+}
+// END: get primary key functions
+
+TEST(StateListenerTest, TestStateListenerWeakPointer) {
+ sp<TestStateListener> listener = new TestStateListener();
+ wp<TestStateListener> wListener = listener;
+ listener = nullptr; // let go of listener
+ EXPECT_TRUE(wListener.promote() == nullptr);
+}
+
+TEST(StateManagerTest, TestStateManagerGetInstance) {
+ sp<TestStateListener> listener1 = new TestStateListener();
+ StateManager& mgr = StateManager::getInstance();
+
+ mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener1);
+ EXPECT_EQ(1, mgr.getStateTrackersCount());
+ EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+}
+
+/**
+ * Test registering listeners to StateTrackers
+ *
+ * - StateManager will create a new StateTracker if it doesn't already exist
+ * and then register the listener to the StateTracker.
+ * - If a listener is already registered to a StateTracker, it is not added again.
+ * - StateTrackers are only created for atoms that are state atoms.
+ */
+TEST(StateTrackerTest, TestRegisterListener) {
+ sp<TestStateListener> listener1 = new TestStateListener();
+ sp<TestStateListener> listener2 = new TestStateListener();
+ StateManager mgr;
+
+ // Register listener to non-existing StateTracker
+ EXPECT_EQ(0, mgr.getStateTrackersCount());
+ mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener1);
+ EXPECT_EQ(1, mgr.getStateTrackersCount());
+ EXPECT_EQ(1, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED));
+
+ // Register listener to existing StateTracker
+ mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener2);
+ EXPECT_EQ(1, mgr.getStateTrackersCount());
+ EXPECT_EQ(2, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED));
+
+ // Register already registered listener to existing StateTracker
+ mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener2);
+ EXPECT_EQ(1, mgr.getStateTrackersCount());
+ EXPECT_EQ(2, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED));
+
+ // Register listener to non-state atom
+ mgr.registerListener(android::util::BATTERY_LEVEL_CHANGED, listener2);
+ EXPECT_EQ(1, mgr.getStateTrackersCount());
+}
+
+/**
+ * Test unregistering listeners from StateTrackers
+ *
+ * - StateManager will unregister listeners from a StateTracker only if the
+ * StateTracker exists and the listener is registered to the StateTracker.
+ * - Once all listeners are removed from a StateTracker, the StateTracker
+ * is also removed.
+ */
+TEST(StateTrackerTest, TestUnregisterListener) {
+ sp<TestStateListener> listener1 = new TestStateListener();
+ sp<TestStateListener> listener2 = new TestStateListener();
+ StateManager mgr;
+
+ // Unregister listener from non-existing StateTracker
+ EXPECT_EQ(0, mgr.getStateTrackersCount());
+ mgr.unregisterListener(android::util::SCREEN_STATE_CHANGED, listener1);
+ EXPECT_EQ(0, mgr.getStateTrackersCount());
+ EXPECT_EQ(-1, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED));
+
+ // Unregister non-registered listener from existing StateTracker
+ mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener1);
+ EXPECT_EQ(1, mgr.getStateTrackersCount());
+ EXPECT_EQ(1, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED));
+ mgr.unregisterListener(android::util::SCREEN_STATE_CHANGED, listener2);
+ EXPECT_EQ(1, mgr.getStateTrackersCount());
+ EXPECT_EQ(1, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED));
+
+ // Unregister second-to-last listener from StateTracker
+ mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener2);
+ mgr.unregisterListener(android::util::SCREEN_STATE_CHANGED, listener1);
+ EXPECT_EQ(1, mgr.getStateTrackersCount());
+ EXPECT_EQ(1, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED));
+
+ // Unregister last listener from StateTracker
+ mgr.unregisterListener(android::util::SCREEN_STATE_CHANGED, listener2);
+ EXPECT_EQ(0, mgr.getStateTrackersCount());
+ EXPECT_EQ(-1, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED));
+}
+
+/**
+ * Test StateManager's onLogEvent and StateListener's onStateChanged correctly
+ * updates listener for states without primary keys.
+ */
+TEST(StateTrackerTest, TestStateChangeNoPrimaryFields) {
+ sp<TestStateListener> listener1 = new TestStateListener();
+ StateManager mgr;
+ mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener1);
+
+ // log event
+ std::shared_ptr<LogEvent> event =
+ buildScreenEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+ mgr.onLogEvent(*event);
+
+ // check listener was updated
+ EXPECT_EQ(1, listener1->updates.size());
+ EXPECT_EQ(DEFAULT_DIMENSION_KEY, listener1->updates[0].mKey);
+ EXPECT_EQ(2, listener1->updates[0].mState);
+
+ // check StateTracker was updated by querying for state
+ HashableDimensionKey queryKey = DEFAULT_DIMENSION_KEY;
+ EXPECT_EQ(2, mgr.getState(android::util::SCREEN_STATE_CHANGED, queryKey));
+}
+
+/**
+ * Test StateManager's onLogEvent and StateListener's onStateChanged correctly
+ * updates listener for states with primary keys.
+ */
+TEST(StateTrackerTest, TestStateChangeOnePrimaryField) {
+ sp<TestStateListener> listener1 = new TestStateListener();
+ StateManager mgr;
+ mgr.registerListener(android::util::UID_PROCESS_STATE_CHANGED, listener1);
+
+ // log event
+ std::shared_ptr<LogEvent> event = buildUidProcessEvent(
+ 1000,
+ android::app::ProcessStateEnum::PROCESS_STATE_TOP); // state value: 1002
+ mgr.onLogEvent(*event);
+
+ // check listener was updated
+ EXPECT_EQ(1, listener1->updates.size());
+ EXPECT_EQ(1000, listener1->updates[0].mKey.getValues()[0].mValue.int_value);
+ EXPECT_EQ(1002, listener1->updates[0].mState);
+
+ // check StateTracker was updated by querying for state
+ HashableDimensionKey queryKey;
+ getUidProcessKey(1000, &queryKey);
+ EXPECT_EQ(1002, mgr.getState(android::util::UID_PROCESS_STATE_CHANGED, queryKey));
+}
+
+TEST(StateTrackerTest, TestStateChangeMultiplePrimaryFields) {
+ sp<TestStateListener> listener1 = new TestStateListener();
+ StateManager mgr;
+ mgr.registerListener(android::util::OVERLAY_STATE_CHANGED, listener1);
+
+ // log event
+ std::shared_ptr<LogEvent> event = buildOverlayEvent(1000, "package1", 1); // state: ENTERED
+ mgr.onLogEvent(*event);
+
+ // check listener update
+ EXPECT_EQ(1, listener1->updates.size());
+ EXPECT_EQ(1000, listener1->updates[0].mKey.getValues()[0].mValue.int_value);
+ EXPECT_EQ(1, listener1->updates[0].mState);
+}
+
+/**
+ * Test StateManager's onLogEvent and StateListener's onStateChanged
+ * when there is an error extracting state from log event. Listener is not
+ * updated of state change.
+ */
+TEST(StateTrackerTest, TestStateChangeEventError) {
+ sp<TestStateListener> listener1 = new TestStateListener();
+ StateManager mgr;
+ mgr.registerListener(android::util::OVERLAY_STATE_CHANGED, listener1);
+
+ // log event
+ std::shared_ptr<LogEvent> event =
+ buildIncorrectOverlayEvent(1000, "package1", 1); // state: ENTERED
+ mgr.onLogEvent(*event);
+
+ // check listener update
+ EXPECT_EQ(0, listener1->updates.size());
+}
+
+TEST(StateTrackerTest, TestStateQuery) {
+ sp<TestStateListener> listener1 = new TestStateListener();
+ sp<TestStateListener> listener2 = new TestStateListener();
+ sp<TestStateListener> listener3 = new TestStateListener();
+ StateManager mgr;
+ mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener1);
+ mgr.registerListener(android::util::UID_PROCESS_STATE_CHANGED, listener2);
+ mgr.registerListener(android::util::OVERLAY_STATE_CHANGED, listener3);
+
+ std::shared_ptr<LogEvent> event1 = buildUidProcessEvent(
+ 1000,
+ android::app::ProcessStateEnum::PROCESS_STATE_TOP); // state value: 1002
+ std::shared_ptr<LogEvent> event2 = buildUidProcessEvent(
+ 1001,
+ android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE); // state value: 1003
+ std::shared_ptr<LogEvent> event3 = buildUidProcessEvent(
+ 1002,
+ android::app::ProcessStateEnum::PROCESS_STATE_PERSISTENT); // state value: 1000
+ std::shared_ptr<LogEvent> event4 = buildUidProcessEvent(
+ 1001,
+ android::app::ProcessStateEnum::PROCESS_STATE_TOP); // state value: 1002
+ std::shared_ptr<LogEvent> event5 =
+ buildScreenEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON); // state value:
+ std::shared_ptr<LogEvent> event6 = buildOverlayEvent(1000, "package1", 1);
+ std::shared_ptr<LogEvent> event7 = buildOverlayEvent(1000, "package2", 2);
+
+ mgr.onLogEvent(*event1);
+ mgr.onLogEvent(*event2);
+ mgr.onLogEvent(*event3);
+ mgr.onLogEvent(*event5);
+ mgr.onLogEvent(*event5);
+ mgr.onLogEvent(*event6);
+ mgr.onLogEvent(*event7);
+
+ // Query for UidProcessState of uid 1001
+ HashableDimensionKey queryKey1;
+ getUidProcessKey(1001, &queryKey1);
+ EXPECT_EQ(1003, mgr.getState(android::util::UID_PROCESS_STATE_CHANGED, queryKey1));
+
+ // Query for UidProcessState of uid 1004 - not in state map
+ HashableDimensionKey queryKey2;
+ getUidProcessKey(1004, &queryKey2);
+ EXPECT_EQ(-1,
+ mgr.getState(android::util::UID_PROCESS_STATE_CHANGED, queryKey2)); // default state
+
+ // Query for UidProcessState of uid 1001 - after change in state
+ mgr.onLogEvent(*event4);
+ EXPECT_EQ(1002, mgr.getState(android::util::UID_PROCESS_STATE_CHANGED, queryKey1));
+
+ // Query for ScreenState
+ EXPECT_EQ(2, mgr.getState(android::util::SCREEN_STATE_CHANGED, DEFAULT_DIMENSION_KEY));
+
+ // Query for OverlayState of uid 1000, package name "package2"
+ HashableDimensionKey queryKey3;
+ getOverlayKey(1000, "package2", &queryKey3);
+ EXPECT_EQ(2, mgr.getState(android::util::OVERLAY_STATE_CHANGED, queryKey3));
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
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/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt
index b4913c87968d..a6e1f0ab0cde 100644
--- a/config/hiddenapi-greylist.txt
+++ b/config/hiddenapi-greylist.txt
@@ -1180,4 +1180,283 @@ Lcom/android/server/net/BaseNetworkObserver;-><init>()V
Lcom/android/server/ResettableTimeout$T;-><init>(Lcom/android/server/ResettableTimeout;)V
Lcom/google/android/gles_jni/EGLImpl;-><init>()V
Lcom/google/android/gles_jni/GLImpl;-><init>()V
+Lcom/google/android/mms/ContentType;->getAudioTypes()Ljava/util/ArrayList;
+Lcom/google/android/mms/ContentType;->getImageTypes()Ljava/util/ArrayList;
+Lcom/google/android/mms/ContentType;->getVideoTypes()Ljava/util/ArrayList;
+Lcom/google/android/mms/ContentType;->isAudioType(Ljava/lang/String;)Z
+Lcom/google/android/mms/ContentType;->isDrmType(Ljava/lang/String;)Z
+Lcom/google/android/mms/ContentType;->isImageType(Ljava/lang/String;)Z
+Lcom/google/android/mms/ContentType;->isSupportedAudioType(Ljava/lang/String;)Z
+Lcom/google/android/mms/ContentType;->isSupportedImageType(Ljava/lang/String;)Z
+Lcom/google/android/mms/ContentType;->isSupportedType(Ljava/lang/String;)Z
+Lcom/google/android/mms/ContentType;->isSupportedVideoType(Ljava/lang/String;)Z
+Lcom/google/android/mms/ContentType;->isTextType(Ljava/lang/String;)Z
+Lcom/google/android/mms/ContentType;->isVideoType(Ljava/lang/String;)Z
+Lcom/google/android/mms/InvalidHeaderValueException;-><init>(Ljava/lang/String;)V
+Lcom/google/android/mms/MmsException;-><init>()V
+Lcom/google/android/mms/MmsException;-><init>(Ljava/lang/String;)V
+Lcom/google/android/mms/MmsException;-><init>(Ljava/lang/String;Ljava/lang/Throwable;)V
+Lcom/google/android/mms/MmsException;-><init>(Ljava/lang/Throwable;)V
+Lcom/google/android/mms/pdu/AcknowledgeInd;-><init>(I[B)V
+Lcom/google/android/mms/pdu/AcknowledgeInd;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
+Lcom/google/android/mms/pdu/AcknowledgeInd;->setReportAllowed(I)V
+Lcom/google/android/mms/pdu/AcknowledgeInd;->setTransactionId([B)V
+Lcom/google/android/mms/pdu/Base64;->decodeBase64([B)[B
+Lcom/google/android/mms/pdu/CharacterSets;->getMibEnumValue(Ljava/lang/String;)I
+Lcom/google/android/mms/pdu/CharacterSets;->getMimeName(I)Ljava/lang/String;
+Lcom/google/android/mms/pdu/DeliveryInd;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
+Lcom/google/android/mms/pdu/DeliveryInd;->getDate()J
+Lcom/google/android/mms/pdu/DeliveryInd;->getMessageId()[B
+Lcom/google/android/mms/pdu/DeliveryInd;->getStatus()I
+Lcom/google/android/mms/pdu/DeliveryInd;->getTo()[Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/EncodedStringValue;-><init>(I[B)V
+Lcom/google/android/mms/pdu/EncodedStringValue;-><init>(Ljava/lang/String;)V
+Lcom/google/android/mms/pdu/EncodedStringValue;-><init>([B)V
+Lcom/google/android/mms/pdu/EncodedStringValue;->appendTextString([B)V
+Lcom/google/android/mms/pdu/EncodedStringValue;->concat([Lcom/google/android/mms/pdu/EncodedStringValue;)Ljava/lang/String;
+Lcom/google/android/mms/pdu/EncodedStringValue;->copy(Lcom/google/android/mms/pdu/EncodedStringValue;)Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/EncodedStringValue;->encodeStrings([Ljava/lang/String;)[Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/EncodedStringValue;->extract(Ljava/lang/String;)[Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/EncodedStringValue;->getCharacterSet()I
+Lcom/google/android/mms/pdu/EncodedStringValue;->getString()Ljava/lang/String;
+Lcom/google/android/mms/pdu/EncodedStringValue;->getTextString()[B
+Lcom/google/android/mms/pdu/EncodedStringValue;->setCharacterSet(I)V
+Lcom/google/android/mms/pdu/EncodedStringValue;->setTextString([B)V
+Lcom/google/android/mms/pdu/GenericPdu;-><init>()V
+Lcom/google/android/mms/pdu/GenericPdu;->getFrom()Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/GenericPdu;->getMessageType()I
+Lcom/google/android/mms/pdu/GenericPdu;->getPduHeaders()Lcom/google/android/mms/pdu/PduHeaders;
+Lcom/google/android/mms/pdu/GenericPdu;->mPduHeaders:Lcom/google/android/mms/pdu/PduHeaders;
+Lcom/google/android/mms/pdu/GenericPdu;->setFrom(Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/GenericPdu;->setMessageType(I)V
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;-><init>()V
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;-><init>(Lcom/google/android/mms/pdu/PduHeaders;Lcom/google/android/mms/pdu/PduBody;)V
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->addTo(Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getBody()Lcom/google/android/mms/pdu/PduBody;
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getDate()J
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getPriority()I
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getSubject()Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getTo()[Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setBody(Lcom/google/android/mms/pdu/PduBody;)V
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setDate(J)V
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setPriority(I)V
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setSubject(Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/NotificationInd;-><init>()V
+Lcom/google/android/mms/pdu/NotificationInd;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
+Lcom/google/android/mms/pdu/NotificationInd;->getContentClass()I
+Lcom/google/android/mms/pdu/NotificationInd;->getContentLocation()[B
+Lcom/google/android/mms/pdu/NotificationInd;->getDeliveryReport()I
+Lcom/google/android/mms/pdu/NotificationInd;->getExpiry()J
+Lcom/google/android/mms/pdu/NotificationInd;->getFrom()Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/NotificationInd;->getMessageClass()[B
+Lcom/google/android/mms/pdu/NotificationInd;->getMessageSize()J
+Lcom/google/android/mms/pdu/NotificationInd;->getSubject()Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/NotificationInd;->getTransactionId()[B
+Lcom/google/android/mms/pdu/NotificationInd;->setContentClass(I)V
+Lcom/google/android/mms/pdu/NotificationInd;->setContentLocation([B)V
+Lcom/google/android/mms/pdu/NotificationInd;->setDeliveryReport(I)V
+Lcom/google/android/mms/pdu/NotificationInd;->setExpiry(J)V
+Lcom/google/android/mms/pdu/NotificationInd;->setFrom(Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/NotificationInd;->setMessageClass([B)V
+Lcom/google/android/mms/pdu/NotificationInd;->setMessageSize(J)V
+Lcom/google/android/mms/pdu/NotificationInd;->setSubject(Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/NotificationInd;->setTransactionId([B)V
+Lcom/google/android/mms/pdu/NotifyRespInd;-><init>(I[BI)V
+Lcom/google/android/mms/pdu/NotifyRespInd;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
+Lcom/google/android/mms/pdu/NotifyRespInd;->setReportAllowed(I)V
+Lcom/google/android/mms/pdu/NotifyRespInd;->setStatus(I)V
+Lcom/google/android/mms/pdu/NotifyRespInd;->setTransactionId([B)V
+Lcom/google/android/mms/pdu/PduBody;-><init>()V
+Lcom/google/android/mms/pdu/PduBody;->addPart(ILcom/google/android/mms/pdu/PduPart;)V
+Lcom/google/android/mms/pdu/PduBody;->addPart(Lcom/google/android/mms/pdu/PduPart;)Z
+Lcom/google/android/mms/pdu/PduBody;->getPart(I)Lcom/google/android/mms/pdu/PduPart;
+Lcom/google/android/mms/pdu/PduBody;->getPartByContentId(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart;
+Lcom/google/android/mms/pdu/PduBody;->getPartByContentLocation(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart;
+Lcom/google/android/mms/pdu/PduBody;->getPartByFileName(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart;
+Lcom/google/android/mms/pdu/PduBody;->getPartByName(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart;
+Lcom/google/android/mms/pdu/PduBody;->getPartIndex(Lcom/google/android/mms/pdu/PduPart;)I
+Lcom/google/android/mms/pdu/PduBody;->getPartsNum()I
+Lcom/google/android/mms/pdu/PduBody;->removePart(I)Lcom/google/android/mms/pdu/PduPart;
+Lcom/google/android/mms/pdu/PduComposer$BufferStack;->copy()V
+Lcom/google/android/mms/pdu/PduComposer$BufferStack;->mark()Lcom/google/android/mms/pdu/PduComposer$PositionMarker;
+Lcom/google/android/mms/pdu/PduComposer$BufferStack;->newbuf()V
+Lcom/google/android/mms/pdu/PduComposer$BufferStack;->pop()V
+Lcom/google/android/mms/pdu/PduComposer$PositionMarker;->getLength()I
+Lcom/google/android/mms/pdu/PduComposer;-><init>(Landroid/content/Context;Lcom/google/android/mms/pdu/GenericPdu;)V
+Lcom/google/android/mms/pdu/PduComposer;->appendEncodedString(Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/PduComposer;->appendHeader(I)I
+Lcom/google/android/mms/pdu/PduComposer;->appendLongInteger(J)V
+Lcom/google/android/mms/pdu/PduComposer;->appendOctet(I)V
+Lcom/google/android/mms/pdu/PduComposer;->appendQuotedString(Ljava/lang/String;)V
+Lcom/google/android/mms/pdu/PduComposer;->appendQuotedString([B)V
+Lcom/google/android/mms/pdu/PduComposer;->appendShortInteger(I)V
+Lcom/google/android/mms/pdu/PduComposer;->appendTextString(Ljava/lang/String;)V
+Lcom/google/android/mms/pdu/PduComposer;->appendTextString([B)V
+Lcom/google/android/mms/pdu/PduComposer;->appendUintvarInteger(J)V
+Lcom/google/android/mms/pdu/PduComposer;->appendValueLength(J)V
+Lcom/google/android/mms/pdu/PduComposer;->arraycopy([BII)V
+Lcom/google/android/mms/pdu/PduComposer;->make()[B
+Lcom/google/android/mms/pdu/PduComposer;->mContentTypeMap:Ljava/util/HashMap;
+Lcom/google/android/mms/pdu/PduComposer;->mMessage:Ljava/io/ByteArrayOutputStream;
+Lcom/google/android/mms/pdu/PduComposer;->mPdu:Lcom/google/android/mms/pdu/GenericPdu;
+Lcom/google/android/mms/pdu/PduComposer;->mPduHeader:Lcom/google/android/mms/pdu/PduHeaders;
+Lcom/google/android/mms/pdu/PduComposer;->mPosition:I
+Lcom/google/android/mms/pdu/PduComposer;->mResolver:Landroid/content/ContentResolver;
+Lcom/google/android/mms/pdu/PduComposer;->mStack:Lcom/google/android/mms/pdu/PduComposer$BufferStack;
+Lcom/google/android/mms/pdu/PduContentTypes;->contentTypes:[Ljava/lang/String;
+Lcom/google/android/mms/pdu/PduHeaders;-><init>()V
+Lcom/google/android/mms/pdu/PduHeaders;->appendEncodedStringValue(Lcom/google/android/mms/pdu/EncodedStringValue;I)V
+Lcom/google/android/mms/pdu/PduHeaders;->getEncodedStringValue(I)Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/PduHeaders;->getEncodedStringValues(I)[Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/PduHeaders;->getLongInteger(I)J
+Lcom/google/android/mms/pdu/PduHeaders;->getOctet(I)I
+Lcom/google/android/mms/pdu/PduHeaders;->getTextString(I)[B
+Lcom/google/android/mms/pdu/PduHeaders;->setEncodedStringValue(Lcom/google/android/mms/pdu/EncodedStringValue;I)V
+Lcom/google/android/mms/pdu/PduHeaders;->setLongInteger(JI)V
+Lcom/google/android/mms/pdu/PduHeaders;->setOctet(II)V
+Lcom/google/android/mms/pdu/PduParser;-><init>([BZ)V
+Lcom/google/android/mms/pdu/PduParser;->checkPartPosition(Lcom/google/android/mms/pdu/PduPart;)I
+Lcom/google/android/mms/pdu/PduParser;->log(Ljava/lang/String;)V
+Lcom/google/android/mms/pdu/PduParser;->parse()Lcom/google/android/mms/pdu/GenericPdu;
+Lcom/google/android/mms/pdu/PduParser;->parseContentType(Ljava/io/ByteArrayInputStream;Ljava/util/HashMap;)[B
+Lcom/google/android/mms/pdu/PduParser;->parsePartHeaders(Ljava/io/ByteArrayInputStream;Lcom/google/android/mms/pdu/PduPart;I)Z
+Lcom/google/android/mms/pdu/PduParser;->parseShortInteger(Ljava/io/ByteArrayInputStream;)I
+Lcom/google/android/mms/pdu/PduParser;->parseUnsignedInt(Ljava/io/ByteArrayInputStream;)I
+Lcom/google/android/mms/pdu/PduParser;->parseValueLength(Ljava/io/ByteArrayInputStream;)I
+Lcom/google/android/mms/pdu/PduParser;->parseWapString(Ljava/io/ByteArrayInputStream;I)[B
+Lcom/google/android/mms/pdu/PduPart;-><init>()V
+Lcom/google/android/mms/pdu/PduPart;->generateLocation()Ljava/lang/String;
+Lcom/google/android/mms/pdu/PduPart;->getCharset()I
+Lcom/google/android/mms/pdu/PduPart;->getContentDisposition()[B
+Lcom/google/android/mms/pdu/PduPart;->getContentId()[B
+Lcom/google/android/mms/pdu/PduPart;->getContentLocation()[B
+Lcom/google/android/mms/pdu/PduPart;->getContentTransferEncoding()[B
+Lcom/google/android/mms/pdu/PduPart;->getContentType()[B
+Lcom/google/android/mms/pdu/PduPart;->getData()[B
+Lcom/google/android/mms/pdu/PduPart;->getDataLength()I
+Lcom/google/android/mms/pdu/PduPart;->getDataUri()Landroid/net/Uri;
+Lcom/google/android/mms/pdu/PduPart;->getFilename()[B
+Lcom/google/android/mms/pdu/PduPart;->getName()[B
+Lcom/google/android/mms/pdu/PduPart;->setCharset(I)V
+Lcom/google/android/mms/pdu/PduPart;->setContentDisposition([B)V
+Lcom/google/android/mms/pdu/PduPart;->setContentId([B)V
+Lcom/google/android/mms/pdu/PduPart;->setContentLocation([B)V
+Lcom/google/android/mms/pdu/PduPart;->setContentTransferEncoding([B)V
+Lcom/google/android/mms/pdu/PduPart;->setContentType([B)V
+Lcom/google/android/mms/pdu/PduPart;->setData([B)V
+Lcom/google/android/mms/pdu/PduPart;->setDataUri(Landroid/net/Uri;)V
+Lcom/google/android/mms/pdu/PduPart;->setFilename([B)V
+Lcom/google/android/mms/pdu/PduPart;->setName([B)V
+Lcom/google/android/mms/pdu/PduPersister;->ADDRESS_FIELDS:[I
+Lcom/google/android/mms/pdu/PduPersister;->CHARSET_COLUMN_NAME_MAP:Ljava/util/HashMap;
+Lcom/google/android/mms/pdu/PduPersister;->ENCODED_STRING_COLUMN_NAME_MAP:Ljava/util/HashMap;
+Lcom/google/android/mms/pdu/PduPersister;->getByteArrayFromPartColumn(Landroid/database/Cursor;I)[B
+Lcom/google/android/mms/pdu/PduPersister;->getBytes(Ljava/lang/String;)[B
+Lcom/google/android/mms/pdu/PduPersister;->getIntegerFromPartColumn(Landroid/database/Cursor;I)Ljava/lang/Integer;
+Lcom/google/android/mms/pdu/PduPersister;->getPartContentType(Lcom/google/android/mms/pdu/PduPart;)Ljava/lang/String;
+Lcom/google/android/mms/pdu/PduPersister;->getPduPersister(Landroid/content/Context;)Lcom/google/android/mms/pdu/PduPersister;
+Lcom/google/android/mms/pdu/PduPersister;->getPendingMessages(J)Landroid/database/Cursor;
+Lcom/google/android/mms/pdu/PduPersister;->load(Landroid/net/Uri;)Lcom/google/android/mms/pdu/GenericPdu;
+Lcom/google/android/mms/pdu/PduPersister;->loadRecipients(ILjava/util/HashSet;Ljava/util/HashMap;Z)V
+Lcom/google/android/mms/pdu/PduPersister;->LONG_COLUMN_NAME_MAP:Ljava/util/HashMap;
+Lcom/google/android/mms/pdu/PduPersister;->mContentResolver:Landroid/content/ContentResolver;
+Lcom/google/android/mms/pdu/PduPersister;->mContext:Landroid/content/Context;
+Lcom/google/android/mms/pdu/PduPersister;->MESSAGE_BOX_MAP:Ljava/util/HashMap;
+Lcom/google/android/mms/pdu/PduPersister;->move(Landroid/net/Uri;Landroid/net/Uri;)Landroid/net/Uri;
+Lcom/google/android/mms/pdu/PduPersister;->mTelephonyManager:Landroid/telephony/TelephonyManager;
+Lcom/google/android/mms/pdu/PduPersister;->OCTET_COLUMN_NAME_MAP:Ljava/util/HashMap;
+Lcom/google/android/mms/pdu/PduPersister;->PART_PROJECTION:[Ljava/lang/String;
+Lcom/google/android/mms/pdu/PduPersister;->PDU_CACHE_INSTANCE:Lcom/google/android/mms/util/PduCache;
+Lcom/google/android/mms/pdu/PduPersister;->persist(Lcom/google/android/mms/pdu/GenericPdu;Landroid/net/Uri;ZZLjava/util/HashMap;)Landroid/net/Uri;
+Lcom/google/android/mms/pdu/PduPersister;->persistAddress(JI[Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/PduPersister;->persistPart(Lcom/google/android/mms/pdu/PduPart;JLjava/util/HashMap;)Landroid/net/Uri;
+Lcom/google/android/mms/pdu/PduPersister;->TEXT_STRING_COLUMN_NAME_MAP:Ljava/util/HashMap;
+Lcom/google/android/mms/pdu/PduPersister;->toIsoString([B)Ljava/lang/String;
+Lcom/google/android/mms/pdu/PduPersister;->updateAddress(JI[Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/PduPersister;->updateHeaders(Landroid/net/Uri;Lcom/google/android/mms/pdu/SendReq;)V
+Lcom/google/android/mms/pdu/PduPersister;->updateParts(Landroid/net/Uri;Lcom/google/android/mms/pdu/PduBody;Ljava/util/HashMap;)V
+Lcom/google/android/mms/pdu/QuotedPrintable;->decodeQuotedPrintable([B)[B
+Lcom/google/android/mms/pdu/ReadOrigInd;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
+Lcom/google/android/mms/pdu/ReadOrigInd;->getMessageId()[B
+Lcom/google/android/mms/pdu/ReadOrigInd;->getReadStatus()I
+Lcom/google/android/mms/pdu/ReadRecInd;-><init>(Lcom/google/android/mms/pdu/EncodedStringValue;[BII[Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/ReadRecInd;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
+Lcom/google/android/mms/pdu/ReadRecInd;->getMessageId()[B
+Lcom/google/android/mms/pdu/ReadRecInd;->setDate(J)V
+Lcom/google/android/mms/pdu/RetrieveConf;-><init>()V
+Lcom/google/android/mms/pdu/RetrieveConf;-><init>(Lcom/google/android/mms/pdu/PduHeaders;Lcom/google/android/mms/pdu/PduBody;)V
+Lcom/google/android/mms/pdu/RetrieveConf;->addCc(Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/RetrieveConf;->getCc()[Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/RetrieveConf;->getContentType()[B
+Lcom/google/android/mms/pdu/RetrieveConf;->getDeliveryReport()I
+Lcom/google/android/mms/pdu/RetrieveConf;->getFrom()Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/RetrieveConf;->getMessageClass()[B
+Lcom/google/android/mms/pdu/RetrieveConf;->getMessageId()[B
+Lcom/google/android/mms/pdu/RetrieveConf;->getReadReport()I
+Lcom/google/android/mms/pdu/RetrieveConf;->getRetrieveStatus()I
+Lcom/google/android/mms/pdu/RetrieveConf;->getRetrieveText()Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/RetrieveConf;->getTransactionId()[B
+Lcom/google/android/mms/pdu/RetrieveConf;->setContentType([B)V
+Lcom/google/android/mms/pdu/RetrieveConf;->setDeliveryReport(I)V
+Lcom/google/android/mms/pdu/RetrieveConf;->setFrom(Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/RetrieveConf;->setMessageClass([B)V
+Lcom/google/android/mms/pdu/RetrieveConf;->setMessageId([B)V
+Lcom/google/android/mms/pdu/RetrieveConf;->setReadReport(I)V
+Lcom/google/android/mms/pdu/RetrieveConf;->setRetrieveStatus(I)V
+Lcom/google/android/mms/pdu/RetrieveConf;->setRetrieveText(Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/RetrieveConf;->setTransactionId([B)V
+Lcom/google/android/mms/pdu/SendConf;-><init>()V
+Lcom/google/android/mms/pdu/SendConf;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
+Lcom/google/android/mms/pdu/SendConf;->getMessageId()[B
+Lcom/google/android/mms/pdu/SendConf;->getResponseStatus()I
+Lcom/google/android/mms/pdu/SendConf;->getTransactionId()[B
+Lcom/google/android/mms/pdu/SendReq;-><init>()V
+Lcom/google/android/mms/pdu/SendReq;-><init>(Lcom/google/android/mms/pdu/PduHeaders;Lcom/google/android/mms/pdu/PduBody;)V
+Lcom/google/android/mms/pdu/SendReq;->addBcc(Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/SendReq;->addCc(Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/SendReq;->getBcc()[Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/SendReq;->getCc()[Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/SendReq;->getContentType()[B
+Lcom/google/android/mms/pdu/SendReq;->getDeliveryReport()I
+Lcom/google/android/mms/pdu/SendReq;->getExpiry()J
+Lcom/google/android/mms/pdu/SendReq;->getMessageClass()[B
+Lcom/google/android/mms/pdu/SendReq;->getMessageSize()J
+Lcom/google/android/mms/pdu/SendReq;->getReadReport()I
+Lcom/google/android/mms/pdu/SendReq;->getTransactionId()[B
+Lcom/google/android/mms/pdu/SendReq;->setBcc([Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/SendReq;->setCc([Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/SendReq;->setContentType([B)V
+Lcom/google/android/mms/pdu/SendReq;->setDeliveryReport(I)V
+Lcom/google/android/mms/pdu/SendReq;->setExpiry(J)V
+Lcom/google/android/mms/pdu/SendReq;->setMessageClass([B)V
+Lcom/google/android/mms/pdu/SendReq;->setMessageSize(J)V
+Lcom/google/android/mms/pdu/SendReq;->setReadReport(I)V
+Lcom/google/android/mms/pdu/SendReq;->setTo([Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/SendReq;->setTransactionId([B)V
+Lcom/google/android/mms/util/AbstractCache;-><init>()V
+Lcom/google/android/mms/util/AbstractCache;->get(Ljava/lang/Object;)Ljava/lang/Object;
+Lcom/google/android/mms/util/AbstractCache;->purge(Ljava/lang/Object;)Ljava/lang/Object;
+Lcom/google/android/mms/util/AbstractCache;->purgeAll()V
+Lcom/google/android/mms/util/AbstractCache;->put(Ljava/lang/Object;Ljava/lang/Object;)Z
+Lcom/google/android/mms/util/DownloadDrmHelper;->isDrmConvertNeeded(Ljava/lang/String;)Z
+Lcom/google/android/mms/util/DownloadDrmHelper;->modifyDrmFwLockFileExtension(Ljava/lang/String;)Ljava/lang/String;
+Lcom/google/android/mms/util/DrmConvertSession;->close(Ljava/lang/String;)I
+Lcom/google/android/mms/util/DrmConvertSession;->convert([BI)[B
+Lcom/google/android/mms/util/DrmConvertSession;->open(Landroid/content/Context;Ljava/lang/String;)Lcom/google/android/mms/util/DrmConvertSession;
+Lcom/google/android/mms/util/PduCache;-><init>()V
+Lcom/google/android/mms/util/PduCache;->getInstance()Lcom/google/android/mms/util/PduCache;
+Lcom/google/android/mms/util/PduCache;->isUpdating(Landroid/net/Uri;)Z
+Lcom/google/android/mms/util/PduCache;->purge(Landroid/net/Uri;)Lcom/google/android/mms/util/PduCacheEntry;
+Lcom/google/android/mms/util/PduCache;->purge(Ljava/lang/Object;)Ljava/lang/Object;
+Lcom/google/android/mms/util/PduCache;->purgeAll()V
+Lcom/google/android/mms/util/PduCacheEntry;-><init>(Lcom/google/android/mms/pdu/GenericPdu;IJ)V
+Lcom/google/android/mms/util/PduCacheEntry;->getMessageBox()I
+Lcom/google/android/mms/util/PduCacheEntry;->getPdu()Lcom/google/android/mms/pdu/GenericPdu;
+Lcom/google/android/mms/util/PduCacheEntry;->getThreadId()J
+Lcom/google/android/mms/util/SqliteWrapper;->checkSQLiteException(Landroid/content/Context;Landroid/database/sqlite/SQLiteException;)V
+Lcom/google/android/mms/util/SqliteWrapper;->delete(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;Ljava/lang/String;[Ljava/lang/String;)I
+Lcom/google/android/mms/util/SqliteWrapper;->insert(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;Landroid/content/ContentValues;)Landroid/net/Uri;
+Lcom/google/android/mms/util/SqliteWrapper;->query(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;
+Lcom/google/android/mms/util/SqliteWrapper;->requery(Landroid/content/Context;Landroid/database/Cursor;)Z
+Lcom/google/android/mms/util/SqliteWrapper;->update(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;Landroid/content/ContentValues;Ljava/lang/String;[Ljava/lang/String;)I
Lcom/google/android/util/AbstractMessageParser$Token$Type;->values()[Lcom/google/android/util/AbstractMessageParser$Token$Type;
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/accessibilityservice/AccessibilityGestureInfo.aidl b/core/java/android/accessibilityservice/AccessibilityGestureEvent.aidl
index 2539051f4069..58a9b64ee9c8 100644
--- a/core/java/android/accessibilityservice/AccessibilityGestureInfo.aidl
+++ b/core/java/android/accessibilityservice/AccessibilityGestureEvent.aidl
@@ -16,4 +16,4 @@
package android.accessibilityservice;
-parcelable AccessibilityGestureInfo;
+parcelable AccessibilityGestureEvent;
diff --git a/core/java/android/accessibilityservice/AccessibilityGestureInfo.java b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
index 28c1dea1dbe7..b5401755a3c8 100644
--- a/core/java/android/accessibilityservice/AccessibilityGestureInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
@@ -44,17 +44,17 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
- * This class describes the gesture information including gesture id and which display it happens
+ * This class describes the gesture event including gesture id and which display it happens
* on.
* <p>
* <strong>Note:</strong> Accessibility services setting the
* {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE}
* flag can receive gestures.
*
- * @see AccessibilityService#onGesture(AccessibilityGestureInfo)
+ * @see AccessibilityService#onGesture(AccessibilityGestureEvent)
*/
-public final class AccessibilityGestureInfo implements Parcelable {
+public final class AccessibilityGestureEvent implements Parcelable {
/** @hide */
@IntDef(prefix = { "GESTURE_" }, value = {
@@ -84,12 +84,12 @@ public final class AccessibilityGestureInfo implements Parcelable {
/** @hide */
@TestApi
- public AccessibilityGestureInfo(int gestureId, int displayId) {
+ public AccessibilityGestureEvent(int gestureId, int displayId) {
mGestureId = gestureId;
mDisplayId = displayId;
}
- private AccessibilityGestureInfo(@NonNull Parcel parcel) {
+ private AccessibilityGestureEvent(@NonNull Parcel parcel) {
mGestureId = parcel.readInt();
mDisplayId = parcel.readInt();
}
@@ -117,7 +117,7 @@ public final class AccessibilityGestureInfo implements Parcelable {
@NonNull
@Override
public String toString() {
- StringBuilder stringBuilder = new StringBuilder("AccessibilityGestureInfo[");
+ StringBuilder stringBuilder = new StringBuilder("AccessibilityGestureEvent[");
stringBuilder.append("gestureId: ").append(mGestureId);
stringBuilder.append(", ");
stringBuilder.append("displayId: ").append(mDisplayId);
@@ -142,14 +142,14 @@ public final class AccessibilityGestureInfo implements Parcelable {
/**
* @see Parcelable.Creator
*/
- public static final @NonNull Parcelable.Creator<AccessibilityGestureInfo> CREATOR =
- new Parcelable.Creator<AccessibilityGestureInfo>() {
- public AccessibilityGestureInfo createFromParcel(Parcel parcel) {
- return new AccessibilityGestureInfo(parcel);
+ public static final @NonNull Parcelable.Creator<AccessibilityGestureEvent> CREATOR =
+ new Parcelable.Creator<AccessibilityGestureEvent>() {
+ public AccessibilityGestureEvent createFromParcel(Parcel parcel) {
+ return new AccessibilityGestureEvent(parcel);
}
- public AccessibilityGestureInfo[] newArray(int size) {
- return new AccessibilityGestureInfo[size];
+ public AccessibilityGestureEvent[] newArray(int size) {
+ return new AccessibilityGestureEvent[size];
}
};
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index a8daf91eadc0..d3c274f4bdfe 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -382,7 +382,7 @@ public abstract class AccessibilityService extends Service {
void onServiceConnected();
void init(int connectionId, IBinder windowToken);
/** The detected gesture information for different displays */
- boolean onGesture(AccessibilityGestureInfo gestureInfo);
+ boolean onGesture(AccessibilityGestureEvent gestureInfo);
boolean onKeyEvent(KeyEvent event);
/** Magnification changed callbacks for different displays */
void onMagnificationChanged(int displayId, @NonNull Region region,
@@ -517,7 +517,7 @@ public abstract class AccessibilityService extends Service {
}
/**
- * Called by {@link #onGesture(AccessibilityGestureInfo)} when the user performs a specific
+ * Called by {@link #onGesture(AccessibilityGestureEvent)} when the user performs a specific
* gesture on the default display.
*
* <strong>Note:</strong> To receive gestures an accessibility service must
@@ -528,7 +528,7 @@ public abstract class AccessibilityService extends Service {
* @param gestureId The unique id of the performed gesture.
*
* @return Whether the gesture was handled.
- * @deprecated Override {@link #onGesture(AccessibilityGestureInfo)} instead.
+ * @deprecated Override {@link #onGesture(AccessibilityGestureEvent)} instead.
*
* @see #GESTURE_SWIPE_UP
* @see #GESTURE_SWIPE_UP_AND_LEFT
@@ -564,14 +564,14 @@ public abstract class AccessibilityService extends Service {
* <strong>Note:</strong> The default implementation calls {@link #onGesture(int)} when the
* touch screen is default display.
*
- * @param gestureInfo The information of gesture.
+ * @param gestureEvent The information of gesture.
*
* @return Whether the gesture was handled.
*
*/
- public boolean onGesture(@NonNull AccessibilityGestureInfo gestureInfo) {
- if (gestureInfo.getDisplayId() == Display.DEFAULT_DISPLAY) {
- onGesture(gestureInfo.getGestureId());
+ public boolean onGesture(@NonNull AccessibilityGestureEvent gestureEvent) {
+ if (gestureEvent.getDisplayId() == Display.DEFAULT_DISPLAY) {
+ onGesture(gestureEvent.getGestureId());
}
return false;
}
@@ -1725,8 +1725,8 @@ public abstract class AccessibilityService extends Service {
}
@Override
- public boolean onGesture(AccessibilityGestureInfo gestureInfo) {
- return AccessibilityService.this.onGesture(gestureInfo);
+ public boolean onGesture(AccessibilityGestureEvent gestureEvent) {
+ return AccessibilityService.this.onGesture(gestureEvent);
}
@Override
@@ -1826,7 +1826,7 @@ public abstract class AccessibilityService extends Service {
}
@Override
- public void onGesture(AccessibilityGestureInfo gestureInfo) {
+ public void onGesture(AccessibilityGestureEvent gestureInfo) {
Message message = mCaller.obtainMessageO(DO_ON_GESTURE, gestureInfo);
mCaller.sendMessage(message);
}
@@ -1942,7 +1942,7 @@ public abstract class AccessibilityService extends Service {
case DO_ON_GESTURE: {
if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
- mCallback.onGesture((AccessibilityGestureInfo) message.obj);
+ mCallback.onGesture((AccessibilityGestureEvent) message.obj);
}
} return;
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
index e0d5e4438f23..8ec3aea2728f 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
@@ -20,7 +20,7 @@ import android.accessibilityservice.IAccessibilityServiceConnection;
import android.graphics.Region;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityWindowInfo;
-import android.accessibilityservice.AccessibilityGestureInfo;
+import android.accessibilityservice.AccessibilityGestureEvent;
import android.view.KeyEvent;
/**
@@ -36,7 +36,7 @@ import android.view.KeyEvent;
void onInterrupt();
- void onGesture(in AccessibilityGestureInfo gestureInfo);
+ void onGesture(in AccessibilityGestureEvent gestureEvent);
void clearAccessibilityCache();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index a6784780d72f..1cc849936fde 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3983,24 +3983,14 @@ public final class ActivityThread extends ClientTransactionHandler {
data.info.applicationInfo, data.compatInfo);
Service service = null;
try {
- java.lang.ClassLoader cl = packageInfo.getClassLoader();
- service = packageInfo.getAppFactory()
- .instantiateService(cl, data.info.name, data.intent);
- } catch (Exception e) {
- if (!mInstrumentation.onException(service, e)) {
- throw new RuntimeException(
- "Unable to instantiate service " + data.info.name
- + ": " + e.toString(), e);
- }
- }
-
- try {
if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
- context.setOuterContext(service);
-
Application app = packageInfo.makeApplication(false, mInstrumentation);
+ java.lang.ClassLoader cl = packageInfo.getClassLoader();
+ service = packageInfo.getAppFactory()
+ .instantiateService(cl, data.info.name, data.intent);
+ context.setOuterContext(service);
service.attach(context, this, data.info.name, data.token, app,
ActivityManager.getService());
service.onCreate();
diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java
index 3a95839d2d12..0f9a6e63a1d2 100644
--- a/core/java/android/app/ActivityTransitionState.java
+++ b/core/java/android/app/ActivityTransitionState.java
@@ -197,13 +197,21 @@ class ActivityTransitionState {
mHasExited = false;
ArrayList<String> sharedElementNames = mEnterActivityOptions.getSharedElementNames();
ResultReceiver resultReceiver = mEnterActivityOptions.getResultReceiver();
- if (mEnterActivityOptions.isReturning()) {
+ final boolean isReturning = mEnterActivityOptions.isReturning();
+ if (isReturning) {
restoreExitedViews();
activity.getWindow().getDecorView().setVisibility(View.VISIBLE);
}
mEnterTransitionCoordinator = new EnterTransitionCoordinator(activity,
resultReceiver, sharedElementNames, mEnterActivityOptions.isReturning(),
- mEnterActivityOptions.isCrossTask());
+ mEnterActivityOptions.isCrossTask(),
+ () -> {
+ if (isReturning) {
+ // once it is done transitioning, we don't need the coordinator --
+ // if we kept it around, it could leak Views
+ mEnterTransitionCoordinator = null;
+ }
+ });
if (mEnterActivityOptions.isCrossTask()) {
mExitingFrom = new ArrayList<>(mEnterActivityOptions.getSharedElementNames());
mExitingTo = new ArrayList<>(mEnterActivityOptions.getSharedElementNames());
diff --git a/core/java/android/app/AppCompatCallbacks.java b/core/java/android/app/AppCompatCallbacks.java
index 17697dba9ccd..08c97eb284e3 100644
--- a/core/java/android/app/AppCompatCallbacks.java
+++ b/core/java/android/app/AppCompatCallbacks.java
@@ -19,6 +19,9 @@ package android.app;
import android.compat.Compatibility;
import android.os.Process;
import android.util.Log;
+import android.util.StatsLog;
+
+import com.android.internal.compat.ChangeReporter;
import java.util.Arrays;
@@ -28,10 +31,10 @@ import java.util.Arrays;
* @hide
*/
public final class AppCompatCallbacks extends Compatibility.Callbacks {
-
private static final String TAG = "Compatibility";
private final long[] mDisabledChanges;
+ private final ChangeReporter mChangeReporter;
/**
* Install this class into the current process.
@@ -45,20 +48,29 @@ public final class AppCompatCallbacks extends Compatibility.Callbacks {
private AppCompatCallbacks(long[] disabledChanges) {
mDisabledChanges = Arrays.copyOf(disabledChanges, disabledChanges.length);
Arrays.sort(mDisabledChanges);
+ mChangeReporter = new ChangeReporter();
}
protected void reportChange(long changeId) {
- Log.d(TAG, "Compat change reported: " + changeId + "; UID " + Process.myUid());
- // TODO log via StatsLog
+ reportChange(changeId, StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__LOGGED);
}
protected boolean isChangeEnabled(long changeId) {
if (Arrays.binarySearch(mDisabledChanges, changeId) < 0) {
// Not present in the disabled array
- reportChange(changeId);
+ reportChange(changeId, StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__ENABLED);
return true;
}
+ reportChange(changeId, StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__DISABLED);
return false;
}
+ private void reportChange(long changeId, int state) {
+ int uid = Process.myUid();
+ //TODO(b/138374585): Implement rate limiting for the logs.
+ Log.d(TAG, ChangeReporter.createLogString(uid, changeId, state));
+ mChangeReporter.reportChange(uid, changeId,
+ state, /* source */StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__SOURCE__APP_PROCESS);
+ }
+
}
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index bb87d96b742c..563174bb9534 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,
@@ -2668,7 +2671,7 @@ public class AppOpsManager {
* @return The proxy UID.
*/
public int getProxyUid() {
- return (int) findFirstNonNegativeForFlagsInStates(mDurations,
+ return (int) findFirstNonNegativeForFlagsInStates(mProxyUids,
MAX_PRIORITY_UID_STATE, MIN_PRIORITY_UID_STATE, OP_FLAGS_ALL);
}
@@ -2690,7 +2693,7 @@ public class AppOpsManager {
* @return The proxy UID.
*/
public int getProxyUid(@UidState int uidState, @OpFlags int flags) {
- return (int) findFirstNonNegativeForFlagsInStates(mDurations,
+ return (int) findFirstNonNegativeForFlagsInStates(mProxyUids,
uidState, uidState, flags);
}
@@ -4251,8 +4254,8 @@ public class AppOpsManager {
* end UID states.
*
* @param counts The data array.
- * @param beginUidState The beginning UID state (exclusive).
- * @param endUidState The end UID state.
+ * @param beginUidState The beginning UID state (inclusive).
+ * @param endUidState The end UID state (inclusive).
* @param flags The UID flags.
* @return The sum.
*/
@@ -4281,13 +4284,13 @@ public class AppOpsManager {
* end UID states.
*
* @param counts The data array.
+ * @param beginUidState The beginning UID state (inclusive).
+ * @param endUidState The end UID state (inclusive).
* @param flags The UID flags.
- * @param beginUidState The beginning UID state (exclusive).
- * @param endUidState The end UID state.
* @return The non-negative value or -1.
*/
private static long findFirstNonNegativeForFlagsInStates(@Nullable LongSparseLongArray counts,
- @OpFlags int flags, @UidState int beginUidState, @UidState int endUidState) {
+ @UidState int beginUidState, @UidState int endUidState, @OpFlags int flags) {
if (counts == null) {
return -1;
}
@@ -4313,14 +4316,14 @@ public class AppOpsManager {
* end UID states.
*
* @param counts The data array.
+ * @param beginUidState The beginning UID state (inclusive).
+ * @param endUidState The end UID state (inclusive).
* @param flags The UID flags.
- * @param beginUidState The beginning UID state (exclusive).
- * @param endUidState The end UID state.
* @return The non-negative value or -1.
*/
private static @Nullable String findFirstNonNullForFlagsInStates(
- @Nullable LongSparseArray<String> counts, @OpFlags int flags,
- @UidState int beginUidState, @UidState int endUidState) {
+ @Nullable LongSparseArray<String> counts, @UidState int beginUidState,
+ @UidState int endUidState, @OpFlags int flags) {
if (counts == null) {
return null;
}
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index bce243cc6108..905f47540fd9 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -18,6 +18,7 @@ package android.app;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
+import android.annotation.NonNull;
import android.app.SharedElementCallback.OnSharedElementsReadyListener;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
@@ -66,13 +67,16 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator {
private final boolean mIsCrossTask;
private Drawable mReplacedBackground;
private ArrayList<String> mPendingExitNames;
+ private Runnable mOnTransitionComplete;
- public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver,
- ArrayList<String> sharedElementNames, boolean isReturning, boolean isCrossTask) {
+ EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver,
+ ArrayList<String> sharedElementNames, boolean isReturning, boolean isCrossTask,
+ @NonNull Runnable onTransitionComplete) {
super(activity.getWindow(), sharedElementNames,
getListener(activity, isReturning && !isCrossTask), isReturning);
mActivity = activity;
mIsCrossTask = isCrossTask;
+ mOnTransitionComplete = onTransitionComplete;
setResultReceiver(resultReceiver);
prepareEnter();
Bundle resultReceiverBundle = new Bundle();
@@ -578,6 +582,10 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator {
window.setBackgroundDrawable(null);
}
}
+ if (mOnTransitionComplete != null) {
+ mOnTransitionComplete.run();
+ mOnTransitionComplete = null;
+ }
}
private void sharedElementTransitionStarted() {
@@ -672,6 +680,10 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator {
mBackgroundAnimator.cancel();
mBackgroundAnimator = null;
}
+ if (mOnTransitionComplete != null) {
+ mOnTransitionComplete.run();
+ mOnTransitionComplete = null;
+ }
super.clearState();
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index bb4e99873f26..2f03ed484e96 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -8150,7 +8150,9 @@ public class Notification implements Parcelable
Action action, StandardTemplateParams p) {
final boolean tombstone = (action.actionIntent == null);
container.setViewVisibility(buttonId, View.VISIBLE);
- container.setImageViewIcon(buttonId, action.getIcon());
+ if (buttonId != R.id.media_seamless) {
+ container.setImageViewIcon(buttonId, action.getIcon());
+ }
// If the action buttons should not be tinted, then just use the default
// notification color. Otherwise, just use the passed-in color.
@@ -8204,6 +8206,10 @@ public class Notification implements Parcelable
view.setViewVisibility(MEDIA_BUTTON_IDS[i], View.GONE);
}
}
+ bindMediaActionButton(view, R.id.media_seamless, new Action(
+ R.drawable.ic_media_seamless, mBuilder.mContext.getString(
+ com.android.internal.R.string.ext_media_seamless_action), null), p);
+ view.setViewVisibility(R.id.media_seamless, View.GONE);
handleImage(view);
// handle the content margin
int endMargin = R.dimen.notification_content_margin_end;
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index f9b96c50e0b8..fd93450c29f0 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -16,7 +16,7 @@
package android.app;
-import android.accessibilityservice.AccessibilityGestureInfo;
+import android.accessibilityservice.AccessibilityGestureEvent;
import android.accessibilityservice.AccessibilityService.Callbacks;
import android.accessibilityservice.AccessibilityService.IAccessibilityServiceClientWrapper;
import android.accessibilityservice.AccessibilityServiceInfo;
@@ -1246,7 +1246,7 @@ public final class UiAutomation {
}
@Override
- public boolean onGesture(AccessibilityGestureInfo gestureInfo) {
+ public boolean onGesture(AccessibilityGestureEvent gestureEvent) {
/* do nothing */
return false;
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index a136bbda2a3e..02cac231f372 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2531,14 +2531,13 @@ public class DevicePolicyManager {
public static final int PASSWORD_QUALITY_ALPHANUMERIC = 0x50000;
/**
- * Constant for {@link #setPasswordQuality}: the user must have entered a
- * password containing at least a letter, a numerical digit and a special
- * symbol, by default. With this password quality, passwords can be
- * restricted to contain various sets of characters, like at least an
- * uppercase letter, etc. These are specified using various methods,
- * like {@link #setPasswordMinimumLowerCase(ComponentName, int)}. Note
- * that quality constants are ordered so that higher values are more
- * restrictive.
+ * Constant for {@link #setPasswordQuality}: allows the admin to set precisely how many
+ * characters of various types the password should contain to satisfy the policy. The admin
+ * should set these requirements via {@link #setPasswordMinimumLetters},
+ * {@link #setPasswordMinimumNumeric}, {@link #setPasswordMinimumSymbols},
+ * {@link #setPasswordMinimumUpperCase}, {@link #setPasswordMinimumLowerCase},
+ * {@link #setPasswordMinimumNonLetter}, and {@link #setPasswordMinimumLength}.
+ * Note that quality constants are ordered so that higher values are more restrictive.
*/
public static final int PASSWORD_QUALITY_COMPLEX = 0x60000;
@@ -3186,10 +3185,7 @@ public class DevicePolicyManager {
* same as any password in the history. Note that the current password will remain until the
* user has set a new one, so the change does not take place immediately. To prompt the user for
* a new password, use {@link #ACTION_SET_NEW_PASSWORD} or
- * {@link #ACTION_SET_NEW_PARENT_PROFILE_PASSWORD} after setting this value. This constraint is
- * only imposed if the administrator has also requested either {@link #PASSWORD_QUALITY_NUMERIC}
- * , {@link #PASSWORD_QUALITY_NUMERIC_COMPLEX} {@link #PASSWORD_QUALITY_ALPHABETIC}, or
- * {@link #PASSWORD_QUALITY_ALPHANUMERIC} with {@link #setPasswordQuality}.
+ * {@link #ACTION_SET_NEW_PARENT_PROFILE_PASSWORD} after setting this value.
* <p>
* On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
* password history length is always 0.
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/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index c74daa8eadfc..3933e81c732a 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -519,9 +519,9 @@ public class LauncherApps {
* <li>The app is a system app.</li>
* <li>The app doesn't request any <a href="/guide/topics/permissions/overview">permissions</a>.
* </li>
- * <li>The <code>&lt;application&gt;</code> tag in the app's manifest doesn't contain any child
- * elements that represent
- * <a href="/guide/components/fundamentals#DeclaringComponents">app components</a>.</li>
+ * <li>The app doesn't have a <em>launcher activity</em> that is enabled by default. A launcher
+ * activity has an intent containing the <code>ACTION_MAIN</code> action and the
+ * <code>CATEGORY_LAUNCHER</code> category.</li>
* </ul>
*
* <p>Additionally, the system hides synthesized activities for some or all apps in the
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index c561013e6768..8dfe00a8adf6 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -39,7 +39,7 @@ import android.app.PackageInstallObserver;
import android.app.admin.DevicePolicyManager;
import android.app.usage.StorageStatsManager;
import android.compat.annotation.ChangeId;
-import android.compat.annotation.EnabledAfter;
+import android.compat.annotation.Disabled;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -3379,7 +3379,7 @@ public abstract class PackageManager {
* @hide
*/
@ChangeId
- @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
+ @Disabled
public static final long FILTER_APPLICATION_QUERY = 135549675L;
/** {@hide} */
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index de61d70a9260..24ee21360ed8 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -16,6 +16,7 @@
package android.content.pm;
+import android.annotation.AppIdInt;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -321,25 +322,24 @@ public abstract class PackageManagerInternal {
Bundle verificationBundle, int userId);
/**
- * Grants access to the package metadata for an ephemeral application.
+ * Grants implicit access based on an interaction between two apps. This grants the target app
+ * access to the calling application's package metadata.
* <p>
- * When an ephemeral application explicitly tries to interact with a full
- * install application [via an activity, service or provider that has been
- * exposed using the {@code visibleToInstantApp} attribute], the normal
- * application must be able to see metadata about the connecting ephemeral
- * app. If the ephemeral application uses an implicit intent [ie action VIEW,
- * category BROWSABLE], it remains hidden from the launched activity.
+ * When an application explicitly tries to interact with another application [via an
+ * activity, service or provider that is either declared in the caller's
+ * manifest via the {@code <queries>} tag or has been exposed via the target apps manifest using
+ * the {@code visibleToInstantApp} attribute], the target application must be able to see
+ * metadata about the calling app. If the calling application uses an implicit intent [ie
+ * action VIEW, category BROWSABLE], it remains hidden from the launched app.
* <p>
- * If the {@code sourceUid} is not for an ephemeral app or {@code targetUid}
- * is not for a fully installed app, this method will be a no-op.
- *
* @param userId the user
* @param intent the intent that triggered the grant
- * @param targetAppId The app ID of the fully installed application
- * @param ephemeralAppId The app ID of the ephemeral application
+ * @param callingAppId The app ID of the calling application
+ * @param targetAppId The app ID of the target application
*/
- public abstract void grantEphemeralAccess(int userId, Intent intent,
- int targetAppId, int ephemeralAppId);
+ public abstract void grantImplicitAccess(
+ @UserIdInt int userId, Intent intent, @AppIdInt int callingAppId,
+ @AppIdInt int targetAppId);
public abstract boolean isInstantAppInstallerComponent(ComponentName component);
/**
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/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 0714884e347c..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,7 +1252,9 @@ 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 from this CameraDevice will be automatically removed when the
* CameraDevice is closed or the application is disconnected from the camera.</p>
@@ -1275,7 +1280,7 @@ public abstract class CameraDevice implements AutoCloseable {
* <p>Application can use this method to retrieve the system-wide camera audio restriction
* settings described in {@link #setCameraAudioRestriction}.</p>
*
- * @return The system-wide mute mode setting resulting from this call
+ * @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
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 4494d27330b5..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() {
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/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/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index 271020db0ccd..bcb94ce2d2d5 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -79,6 +79,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable {
/**
* Wrapped {@link ParcelFileDescriptor}, if any. Used to avoid
* double-closing {@link #mFd}.
+ * mClosed is always true if mWrapped is non-null.
*/
private final ParcelFileDescriptor mWrapped;
@@ -1023,6 +1024,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable {
}
try {
if (!mClosed) {
+ // mWrapped was and is null.
closeWithStatus(Status.LEAKED, null);
}
} finally {
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 30e59590022c..76e728a11e83 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -182,6 +182,12 @@ public class Process {
*/
public static final int NETWORK_STACK_UID = 1073;
+ /**
+ * Defines the UID/GID for fs-verity certificate ownership in keystore.
+ * @hide
+ */
+ public static final int FSVERITY_CERT_UID = 1075;
+
/** {@hide} */
public static final int NOBODY_UID = 9999;
diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java
index cdae72ebd313..b6af82948da3 100644
--- a/core/java/android/os/SystemProperties.java
+++ b/core/java/android/os/SystemProperties.java
@@ -189,6 +189,8 @@ public class SystemProperties {
* Set the value for the given {@code key} to {@code val}.
*
* @throws IllegalArgumentException if the {@code val} exceeds 91 characters
+ * @throws RuntimeException if the property cannot be set, for example, if it was blocked by
+ * SELinux. libc will log the underlying reason.
* @hide
*/
@UnsupportedAppUsage
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/SearchIndexablesContract.java b/core/java/android/provider/SearchIndexablesContract.java
index 5f8266d2b579..298628e7578b 100644
--- a/core/java/android/provider/SearchIndexablesContract.java
+++ b/core/java/android/provider/SearchIndexablesContract.java
@@ -84,9 +84,9 @@ public class SearchIndexablesContract {
/**
* Last path segment for Preference Key, Slice Uri pair.
* <p>
- * The (Key, Slice Uri) pairs are a mapping between the primary key of the search result and
- * a Uri for a Slice that represents the same data. Thus, an app can specify a list of Uris
- * for Slices that replace regular intent-based search results with inline content.
+ * The (Key, Slice Uri) pairs are a mapping between the primary key of the search result and
+ * a Uri for a Slice that represents the same data. Thus, an app can specify a list of Uris
+ * for Slices that replace regular intent-based search results with inline content.
* </p>
*/
public static final String SLICE_URI_PAIRS = "slice_uri_pairs";
@@ -96,6 +96,22 @@ public class SearchIndexablesContract {
*/
public static final String SLICE_URI_PAIRS_PATH = SETTINGS + "/" + SLICE_URI_PAIRS;
+
+ /**
+ * Dynamic indexable raw data names.
+ *
+ * @hide
+ */
+ public static final String DYNAMIC_INDEXABLES_RAW = "dynamic_indexables_raw";
+
+ /**
+ * ContentProvider path for dynamic indexable raw data.
+ *
+ * @hide
+ */
+ public static final String DYNAMIC_INDEXABLES_RAW_PATH =
+ SETTINGS + "/" + DYNAMIC_INDEXABLES_RAW;
+
/**
* Indexable xml resources columns.
*/
@@ -212,7 +228,7 @@ public class SearchIndexablesContract {
* Cursor schema for SliceUriPairs.
*/
@NonNull
- public static final String[] SLICE_URI_PAIRS_COLUMNS = new String[]{
+ public static final String[] SLICE_URI_PAIRS_COLUMNS = new String[] {
SliceUriPairColumns.KEY,
SliceUriPairColumns.SLICE_URI
};
diff --git a/core/java/android/provider/SearchIndexablesProvider.java b/core/java/android/provider/SearchIndexablesProvider.java
index da29e2e5e39f..68284b4895c3 100644
--- a/core/java/android/provider/SearchIndexablesProvider.java
+++ b/core/java/android/provider/SearchIndexablesProvider.java
@@ -77,6 +77,7 @@ public abstract class SearchIndexablesProvider extends ContentProvider {
private static final int MATCH_NON_INDEXABLE_KEYS_CODE = 3;
private static final int MATCH_SITE_MAP_PAIRS_CODE = 4;
private static final int MATCH_SLICE_URI_PAIRS_CODE = 5;
+ private static final int MATCH_DYNAMIC_RAW_CODE = 6;
/**
* Implementation is provided by the parent class.
@@ -96,6 +97,8 @@ public abstract class SearchIndexablesProvider extends ContentProvider {
MATCH_SITE_MAP_PAIRS_CODE);
mMatcher.addURI(mAuthority, SearchIndexablesContract.SLICE_URI_PAIRS_PATH,
MATCH_SLICE_URI_PAIRS_CODE);
+ mMatcher.addURI(mAuthority, SearchIndexablesContract.DYNAMIC_INDEXABLES_RAW_PATH,
+ MATCH_DYNAMIC_RAW_CODE);
// Sanity check our setup
if (!info.exported) {
@@ -126,6 +129,8 @@ public abstract class SearchIndexablesProvider extends ContentProvider {
return querySiteMapPairs();
case MATCH_SLICE_URI_PAIRS_CODE:
return querySliceUriPairs();
+ case MATCH_DYNAMIC_RAW_CODE:
+ return queryDynamicRawData(null);
default:
throw new UnsupportedOperationException("Unknown Uri " + uri);
}
@@ -191,12 +196,30 @@ public abstract class SearchIndexablesProvider extends ContentProvider {
return null;
}
+ /**
+ * Returns all {@link android.provider.SearchIndexablesContract.RawData}.
+ *
+ * Those are the dynamic raw indexable data.
+ *
+ * @param projection list of {@link android.provider.SearchIndexablesContract.RawData} columns
+ * to put into the cursor. If {@code null} all supported columns should be
+ * included.
+ *
+ * @hide
+ */
+ @Nullable
+ public Cursor queryDynamicRawData(String[] projection) {
+ // By default no-op;
+ return null;
+ }
+
@Override
public String getType(Uri uri) {
switch (mMatcher.match(uri)) {
case MATCH_RES_CODE:
return SearchIndexablesContract.XmlResource.MIME_TYPE;
case MATCH_RAW_CODE:
+ case MATCH_DYNAMIC_RAW_CODE:
return SearchIndexablesContract.RawData.MIME_TYPE;
case MATCH_NON_INDEXABLE_KEYS_CODE:
return SearchIndexablesContract.NonIndexableKey.MIME_TYPE;
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/carrier/ApnService.java b/core/java/android/service/carrier/ApnService.java
index 57e4b1b40748..0c12fd409078 100644
--- a/core/java/android/service/carrier/ApnService.java
+++ b/core/java/android/service/carrier/ApnService.java
@@ -26,7 +26,7 @@ import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
-import com.android.internal.telephony.IApnSourceService;
+import android.service.carrier.IApnSourceService;
import java.util.List;
diff --git a/telephony/java/com/android/internal/telephony/IApnSourceService.aidl b/core/java/android/service/carrier/IApnSourceService.aidl
index 34c9067c3f2f..fadd2ff1a772 100644
--- a/telephony/java/com/android/internal/telephony/IApnSourceService.aidl
+++ b/core/java/android/service/carrier/IApnSourceService.aidl
@@ -14,10 +14,11 @@
* limitations under the License.
*/
-package com.android.internal.telephony;
+package android.service.carrier;
import android.content.ContentValues;
+/** @hide */
interface IApnSourceService {
/** Retreive APNs. */
ContentValues[] getApns(int subId);
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 78e30ab8cdc3..85f13d552fcf 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1522,6 +1522,7 @@ public abstract class NotificationListenerService extends Service {
private ArrayList<Notification.Action> mSmartActions;
private ArrayList<CharSequence> mSmartReplies;
private boolean mCanBubble;
+ private boolean mVisuallyInterruptive;
private static final int PARCEL_VERSION = 2;
@@ -1553,6 +1554,7 @@ public abstract class NotificationListenerService extends Service {
out.writeTypedList(mSmartActions, flags);
out.writeCharSequenceList(mSmartReplies);
out.writeBoolean(mCanBubble);
+ out.writeBoolean(mVisuallyInterruptive);
}
/** @hide */
@@ -1585,6 +1587,7 @@ public abstract class NotificationListenerService extends Service {
mSmartActions = in.createTypedArrayList(Notification.Action.CREATOR);
mSmartReplies = in.readCharSequenceList();
mCanBubble = in.readBoolean();
+ mVisuallyInterruptive = in.readBoolean();
}
@@ -1772,6 +1775,11 @@ public abstract class NotificationListenerService extends Service {
}
/** @hide */
+ public boolean visuallyInterruptive() {
+ return mVisuallyInterruptive;
+ }
+
+ /** @hide */
public boolean isNoisy() {
return mNoisy;
}
@@ -1787,7 +1795,8 @@ public abstract class NotificationListenerService extends Service {
ArrayList<SnoozeCriterion> snoozeCriteria, boolean showBadge,
int userSentiment, boolean hidden, long lastAudiblyAlertedMs,
boolean noisy, ArrayList<Notification.Action> smartActions,
- ArrayList<CharSequence> smartReplies, boolean canBubble) {
+ ArrayList<CharSequence> smartReplies, boolean canBubble,
+ boolean visuallyInterruptive) {
mKey = key;
mRank = rank;
mIsAmbient = importance < NotificationManager.IMPORTANCE_LOW;
@@ -1808,6 +1817,7 @@ public abstract class NotificationListenerService extends Service {
mSmartActions = smartActions;
mSmartReplies = smartReplies;
mCanBubble = canBubble;
+ mVisuallyInterruptive = visuallyInterruptive;
}
/**
@@ -1832,7 +1842,8 @@ public abstract class NotificationListenerService extends Service {
other.mNoisy,
other.mSmartActions,
other.mSmartReplies,
- other.mCanBubble);
+ other.mCanBubble,
+ other.mVisuallyInterruptive);
}
/**
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index bd953cad2b75..94f6a50c1bd9 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -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;
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index 5a2d8f4329dc..1c50d73c4953 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -276,7 +276,7 @@ public class TextLine {
final int runCount = mDirections.getRunCount();
for (int runIndex = 0; runIndex < runCount; runIndex++) {
final int runStart = mDirections.getRunStart(runIndex);
- if (runStart >= mLen) break;
+ if (runStart > mLen) break;
final int runLimit = Math.min(runStart + mDirections.getRunLength(runIndex), mLen);
final boolean runIsRtl = mDirections.isRunRtl(runIndex);
@@ -361,7 +361,7 @@ public class TextLine {
float h = 0;
for (int runIndex = 0; runIndex < mDirections.getRunCount(); runIndex++) {
final int runStart = mDirections.getRunStart(runIndex);
- if (runStart >= mLen) break;
+ if (runStart > mLen) break;
final int runLimit = Math.min(runStart + mDirections.getRunLength(runIndex), mLen);
final boolean runIsRtl = mDirections.isRunRtl(runIndex);
diff --git a/core/java/android/view/IPinnedStackController.aidl b/core/java/android/view/IPinnedStackController.aidl
index d2dcb568ef6c..f1d152ba29af 100644
--- a/core/java/android/view/IPinnedStackController.aidl
+++ b/core/java/android/view/IPinnedStackController.aidl
@@ -16,6 +16,8 @@
package android.view;
+import android.graphics.Rect;
+
/**
* An interface to the PinnedStackController to update it of state changes, and to query
* information based on the current state.
@@ -30,15 +32,17 @@ interface IPinnedStackController {
oneway void setIsMinimized(boolean isMinimized);
/**
- * Notifies the controller of the current min edge size, this is needed to allow the system to
- * properly calculate the aspect ratio of the expanded PIP. The given {@param minEdgeSize} is
- * always bounded to be larger than the default minEdgeSize, so the caller can call this method
- * with 0 to reset to the default size.
+ * @return what WM considers to be the current device rotation.
*/
- oneway void setMinEdgeSize(int minEdgeSize);
+ int getDisplayRotation();
/**
- * @return what WM considers to be the current device rotation.
+ * Notifies the controller to actually start the PiP animation.
+ * The bounds would be calculated based on the last save reentry fraction internally.
+ * {@param destinationBounds} is the stack bounds of the final PiP window
+ * and {@param sourceRectHint} is the source bounds hint used when entering picture-in-picture,
+ * expect the same bound passed via IPinnedStackListener#onPrepareAnimation.
+ * {@param animationDuration} suggests the animation duration transitioning to PiP window.
*/
- int getDisplayRotation();
+ void startAnimation(in Rect destinationBounds, in Rect sourceRectHint, int animationDuration);
}
diff --git a/core/java/android/view/IPinnedStackListener.aidl b/core/java/android/view/IPinnedStackListener.aidl
index 2da353b1f0ee..806d81e39cca 100644
--- a/core/java/android/view/IPinnedStackListener.aidl
+++ b/core/java/android/view/IPinnedStackListener.aidl
@@ -16,8 +16,10 @@
package android.view;
+import android.content.ComponentName;
import android.content.pm.ParceledListSlice;
import android.graphics.Rect;
+import android.view.DisplayInfo;
import android.view.IPinnedStackController;
/**
@@ -36,18 +38,13 @@ oneway interface IPinnedStackListener {
/**
* Called when the window manager has detected a change that would cause the movement bounds
* to be changed (ie. after configuration change, aspect ratio change, etc). It then provides
- * the components that allow the listener to calculate the movement bounds itself. The
- * {@param normalBounds} are also the default bounds that the PiP would be entered in its
- * current state with the aspect ratio applied. The {@param animatingBounds} are provided
- * to indicate the current target bounds of the pinned stack (the final bounds if animating,
- * the current bounds if not), which may be helpful in calculating dependent animation bounds.
- *
- * The {@param displayRotation} is provided so that the client can verify when making certain
- * calls that it will not provide stale information based on an old display rotation (ie. if
- * the WM has changed in the mean time but the client has not received onMovementBoundsChanged).
+ * the components that allow the listener to calculate the movement bounds itself.
+ * The {@param animatingBounds} are provided to indicate the current target bounds of the
+ * pinned stack (the final bounds if animating, the current bounds if not),
+ * which may be helpful in calculating dependent animation bounds.
*/
- void onMovementBoundsChanged(in Rect insetBounds, in Rect normalBounds, in Rect animatingBounds,
- boolean fromImeAdjustment, boolean fromShelfAdjustment, int displayRotation);
+ void onMovementBoundsChanged(in Rect animatingBounds, boolean fromImeAdjustment,
+ boolean fromShelfAdjustment);
/**
* Called when window manager decides to adjust the pinned stack bounds because of the IME, or
@@ -76,4 +73,47 @@ oneway interface IPinnedStackListener {
* is first registered to allow the listener to synchronized its state with the controller.
*/
void onActionsChanged(in ParceledListSlice actions);
+
+ /**
+ * Called by the window manager to notify the listener to save the reentry fraction,
+ * typically when an Activity leaves PiP (picture-in-picture) mode to fullscreen.
+ * {@param componentName} represents the application component of PiP window
+ * while {@param bounds} is the current PiP bounds used to calculate the
+ * reentry snap fraction.
+ */
+ void onSaveReentrySnapFraction(in ComponentName componentName, in Rect bounds);
+
+ /**
+ * Called by the window manager to notify the listener to reset saved reentry fraction,
+ * typically when an Activity enters PiP (picture-in-picture) mode from fullscreen.
+ * {@param componentName} represents the application component of PiP window.
+ */
+ void onResetReentrySnapFraction(in ComponentName componentName);
+
+ /**
+ * Called when the window manager has detected change on DisplayInfo, or
+ * when the listener is first registered to allow the listener to synchronized its state with
+ * the controller.
+ */
+ void onDisplayInfoChanged(in DisplayInfo displayInfo);
+
+ /**
+ * Called by the window manager at the beginning of a configuration update cascade
+ * since the metrics from these resources are used for bounds calculations.
+ */
+ void onConfigurationChanged();
+
+ /**
+ * Called by the window manager when the aspect ratio is reset.
+ */
+ void onAspectRatioChanged(float aspectRatio);
+
+ /**
+ * Called by the window manager to notify the listener to prepare for PiP animation.
+ * Internally, the target bounds would be calculated from the given {@param aspectRatio}
+ * and {@param bounds}, the saved reentry snap fraction also contributes.
+ * Caller would wait for a IPinnedStackController#startAnimation callback to actually
+ * start the animation, see details in IPinnedStackController.
+ */
+ void onPrepareAnimation(in Rect sourceRectHint, float aspectRatio, in Rect bounds);
}
diff --git a/core/java/android/view/IRecentsAnimationRunner.aidl b/core/java/android/view/IRecentsAnimationRunner.aidl
index 724d96ed9585..6eb90fc54286 100644
--- a/core/java/android/view/IRecentsAnimationRunner.aidl
+++ b/core/java/android/view/IRecentsAnimationRunner.aidl
@@ -54,6 +54,6 @@ oneway interface IRecentsAnimationRunner {
*/
@UnsupportedAppUsage
void onAnimationStart(in IRecentsAnimationController controller,
- in RemoteAnimationTarget[] apps, in Rect homeContentInsets,
- in Rect minimizedHomeBounds) = 2;
+ in RemoteAnimationTarget[] apps, in RemoteAnimationTarget[] wallpapers,
+ in Rect homeContentInsets, in Rect minimizedHomeBounds) = 2;
}
diff --git a/core/java/android/view/IRemoteAnimationRunner.aidl b/core/java/android/view/IRemoteAnimationRunner.aidl
index 73b023c99665..7b35aa2d2b45 100644
--- a/core/java/android/view/IRemoteAnimationRunner.aidl
+++ b/core/java/android/view/IRemoteAnimationRunner.aidl
@@ -34,7 +34,7 @@ oneway interface IRemoteAnimationRunner {
* @param finishedCallback The callback to invoke when the animation is finished.
*/
@UnsupportedAppUsage
- void onAnimationStart(in RemoteAnimationTarget[] apps,
+ void onAnimationStart(in RemoteAnimationTarget[] apps, in RemoteAnimationTarget[] wallpapers,
in IRemoteAnimationFinishedCallback finishedCallback);
/**
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/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java
index b2f3f5edcd20..d54e9d58356b 100644
--- a/core/java/android/view/NotificationHeaderView.java
+++ b/core/java/android/view/NotificationHeaderView.java
@@ -30,6 +30,7 @@ import android.graphics.drawable.Drawable;
import android.util.ArraySet;
import android.util.AttributeSet;
import android.widget.ImageView;
+import android.widget.LinearLayout;
import android.widget.RemoteViews;
import com.android.internal.R;
@@ -54,6 +55,7 @@ public class NotificationHeaderView extends ViewGroup {
private OnClickListener mExpandClickListener;
private OnClickListener mAppOpsListener;
private HeaderTouchListener mTouchListener = new HeaderTouchListener();
+ private LinearLayout mTransferChip;
private ImageView mExpandButton;
private CachingIconView mIcon;
private View mProfileBadge;
@@ -116,6 +118,7 @@ public class NotificationHeaderView extends ViewGroup {
mAppName = findViewById(com.android.internal.R.id.app_name_text);
mHeaderText = findViewById(com.android.internal.R.id.header_text);
mSecondaryHeaderText = findViewById(com.android.internal.R.id.header_text_secondary);
+ mTransferChip = findViewById(com.android.internal.R.id.media_seamless);
mExpandButton = findViewById(com.android.internal.R.id.expand_button);
mIcon = findViewById(com.android.internal.R.id.icon);
mProfileBadge = findViewById(com.android.internal.R.id.profile_badge);
@@ -148,9 +151,11 @@ public class NotificationHeaderView extends ViewGroup {
int childHeightSpec = getChildMeasureSpec(wrapContentHeightSpec,
lp.topMargin + lp.bottomMargin, lp.height);
child.measure(childWidthSpec, childHeightSpec);
+ // Icons that should go at the end
if ((child == mExpandButton && mShowExpandButtonAtEnd)
|| child == mProfileBadge
- || child == mAppOps) {
+ || child == mAppOps
+ || child == mTransferChip) {
iconWidth += lp.leftMargin + lp.rightMargin + child.getMeasuredWidth();
} else {
totalWidth += lp.leftMargin + lp.rightMargin + child.getMeasuredWidth();
@@ -211,9 +216,11 @@ public class NotificationHeaderView extends ViewGroup {
int layoutRight;
int top = (int) (getPaddingTop() + (ownHeight - childHeight) / 2.0f);
int bottom = top + childHeight;
+ // Icons that should go at the end
if ((child == mExpandButton && mShowExpandButtonAtEnd)
|| child == mProfileBadge
- || child == mAppOps) {
+ || child == mAppOps
+ || child == mTransferChip) {
if (end == getMeasuredWidth()) {
layoutRight = end - mContentEndMargin;
} else {
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/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 90e69f3abc6c..262b9e50ad00 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -136,6 +136,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
boolean mIsCreating = false;
private volatile boolean mRtHandlingPositionUpdates = false;
+ private volatile boolean mRtReleaseSurfaces = false;
private final ViewTreeObserver.OnScrollChangedListener mScrollChangedListener =
this::updateSurface;
@@ -658,7 +659,14 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
}
private void releaseSurfaces() {
+ mSurfaceAlpha = 1f;
+
synchronized (mSurfaceControlLock) {
+ if (mRtHandlingPositionUpdates) {
+ mRtReleaseSurfaces = true;
+ return;
+ }
+
if (mSurfaceControl != null) {
mTmpTransaction.remove(mSurfaceControl);
mSurfaceControl = null;
@@ -669,7 +677,6 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
}
mTmpTransaction.apply();
}
- mSurfaceAlpha = 1f;
}
/** @hide */
@@ -1084,7 +1091,9 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
// the synchronization would violate the rule that RT must never block
// on the UI thread which would open up potential deadlocks. The risk of
// a single-frame desync is therefore preferable for now.
- mRtHandlingPositionUpdates = true;
+ synchronized(mSurfaceControlLock) {
+ mRtHandlingPositionUpdates = true;
+ }
if (mRTLastReportedPosition.left == left
&& mRTLastReportedPosition.top == top
&& mRTLastReportedPosition.right == right
@@ -1126,6 +1135,18 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
frameNumber);
}
mRtTransaction.hide(mSurfaceControl);
+
+ synchronized (mSurfaceControlLock) {
+ if (mRtReleaseSurfaces) {
+ mRtReleaseSurfaces = false;
+ mRtTransaction.remove(mSurfaceControl);
+ mRtTransaction.remove(mBackgroundControl);
+ mSurfaceControl = null;
+ mBackgroundControl = null;
+ }
+ mRtHandlingPositionUpdates = false;
+ }
+
mRtTransaction.apply();
}
};
diff --git a/core/java/android/view/SyncRtSurfaceTransactionApplier.java b/core/java/android/view/SyncRtSurfaceTransactionApplier.java
index 85457cb48218..a6536f5d83b7 100644
--- a/core/java/android/view/SyncRtSurfaceTransactionApplier.java
+++ b/core/java/android/view/SyncRtSurfaceTransactionApplier.java
@@ -30,6 +30,14 @@ import java.util.function.Consumer;
*/
public class SyncRtSurfaceTransactionApplier {
+ public static final int FLAG_ALL = 0xffffffff;
+ public static final int FLAG_ALPHA = 1;
+ public static final int FLAG_MATRIX = 1 << 1;
+ public static final int FLAG_WINDOW_CROP = 1 << 2;
+ public static final int FLAG_LAYER = 1 << 3;
+ public static final int FLAG_CORNER_RADIUS = 1 << 4;
+ public static final int FLAG_VISIBILITY = 1 << 5;
+
private final Surface mTargetSurface;
private final ViewRootImpl mTargetViewRootImpl;
private final float[] mTmpFloat9 = new float[9];
@@ -72,15 +80,27 @@ public class SyncRtSurfaceTransactionApplier {
}
public static void applyParams(Transaction t, SurfaceParams params, float[] tmpFloat9) {
- t.setMatrix(params.surface, params.matrix, tmpFloat9);
- t.setWindowCrop(params.surface, params.windowCrop);
- t.setAlpha(params.surface, params.alpha);
- t.setLayer(params.surface, params.layer);
- t.setCornerRadius(params.surface, params.cornerRadius);
- if (params.visible) {
- t.show(params.surface);
- } else {
- t.hide(params.surface);
+ if ((params.flags & FLAG_MATRIX) != 0) {
+ t.setMatrix(params.surface, params.matrix, tmpFloat9);
+ }
+ if ((params.flags & FLAG_WINDOW_CROP) != 0) {
+ t.setWindowCrop(params.surface, params.windowCrop);
+ }
+ if ((params.flags & FLAG_ALPHA) != 0) {
+ t.setAlpha(params.surface, params.alpha);
+ }
+ if ((params.flags & FLAG_LAYER) != 0) {
+ t.setLayer(params.surface, params.layer);
+ }
+ if ((params.flags & FLAG_CORNER_RADIUS) != 0) {
+ t.setCornerRadius(params.surface, params.cornerRadius);
+ }
+ if ((params.flags & FLAG_VISIBILITY) != 0) {
+ if (params.visible) {
+ t.show(params.surface);
+ } else {
+ t.hide(params.surface);
+ }
}
}
@@ -115,6 +135,105 @@ public class SyncRtSurfaceTransactionApplier {
public static class SurfaceParams {
+ public static class Builder {
+ final SurfaceControl surface;
+ int flags;
+ float alpha;
+ float cornerRadius;
+ Matrix matrix;
+ Rect windowCrop;
+ int layer;
+ boolean visible;
+
+ /**
+ * @param surface The surface to modify.
+ */
+ public Builder(SurfaceControl surface) {
+ this.surface = surface;
+ }
+
+ /**
+ * @param alpha The alpha value to apply to the surface.
+ * @return this Builder
+ */
+ public Builder withAlpha(float alpha) {
+ this.alpha = alpha;
+ flags |= FLAG_ALPHA;
+ return this;
+ }
+
+ /**
+ * @param matrix The matrix to apply to the surface.
+ * @return this Builder
+ */
+ public Builder withMatrix(Matrix matrix) {
+ this.matrix = matrix;
+ flags |= FLAG_MATRIX;
+ return this;
+ }
+
+ /**
+ * @param windowCrop The window crop to apply to the surface.
+ * @return this Builder
+ */
+ public Builder withWindowCrop(Rect windowCrop) {
+ this.windowCrop = windowCrop;
+ flags |= FLAG_WINDOW_CROP;
+ return this;
+ }
+
+ /**
+ * @param layer The layer to assign the surface.
+ * @return this Builder
+ */
+ public Builder withLayer(int layer) {
+ this.layer = layer;
+ flags |= FLAG_LAYER;
+ return this;
+ }
+
+ /**
+ * @param radius the Radius for rounded corners to apply to the surface.
+ * @return this Builder
+ */
+ public Builder withCornerRadius(float radius) {
+ this.cornerRadius = radius;
+ flags |= FLAG_CORNER_RADIUS;
+ return this;
+ }
+
+ /**
+ * @param visible The visibility to apply to the surface.
+ * @return this Builder
+ */
+ public Builder withVisibility(boolean visible) {
+ this.visible = visible;
+ flags |= FLAG_VISIBILITY;
+ return this;
+ }
+
+ /**
+ * @return a new SurfaceParams instance
+ */
+ public SurfaceParams build() {
+ return new SurfaceParams(surface, flags, alpha, matrix, windowCrop, layer,
+ cornerRadius, visible);
+ }
+ }
+
+ private SurfaceParams(SurfaceControl surface, int params, float alpha, Matrix matrix,
+ Rect windowCrop, int layer, float cornerRadius, boolean visible) {
+ this.flags = params;
+ this.surface = surface;
+ this.alpha = alpha;
+ this.matrix = new Matrix(matrix);
+ this.windowCrop = new Rect(windowCrop);
+ this.layer = layer;
+ this.cornerRadius = cornerRadius;
+ this.visible = visible;
+ }
+
+
/**
* Constructs surface parameters to be applied when the current view state gets pushed to
* RenderThread.
@@ -123,18 +242,27 @@ public class SyncRtSurfaceTransactionApplier {
* @param alpha Alpha to apply.
* @param matrix Matrix to apply.
* @param windowCrop Crop to apply.
+ * @param layer The layer to apply.
+ * @param cornerRadius The corner radius to apply.
+ * @param visible The visibility to apply.
+ *
+ * @deprecated Use {@link SurfaceParams.Builder} to create an instance.
*/
+ @Deprecated
public SurfaceParams(SurfaceControl surface, float alpha, Matrix matrix,
Rect windowCrop, int layer, float cornerRadius, boolean visible) {
+ this.flags = FLAG_ALL;
this.surface = surface;
this.alpha = alpha;
this.matrix = new Matrix(matrix);
- this.windowCrop = new Rect(windowCrop);
+ this.windowCrop = windowCrop != null ? new Rect(windowCrop) : null;
this.layer = layer;
this.cornerRadius = cornerRadius;
this.visible = visible;
}
+ private final int flags;
+
@VisibleForTesting
public final SurfaceControl surface;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index bdfb3e318b3b..cfb6a79a674c 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -14466,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/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java
index 957673dba133..859e9a43f7c2 100644
--- a/core/java/android/view/ViewPropertyAnimator.java
+++ b/core/java/android/view/ViewPropertyAnimator.java
@@ -1138,6 +1138,12 @@ public class ViewPropertyAnimator {
boolean hardwareAccelerated = mView.isHardwareAccelerated();
+ // alpha requires slightly different treatment than the other (transform) properties.
+ // The logic in setAlpha() is not simply setting mAlpha, plus the invalidation
+ // logic is dependent on how the view handles an internal call to onSetAlpha().
+ // We track what kinds of properties are set, and how alpha is handled when it is
+ // set, and perform the invalidation steps appropriately.
+ boolean alphaHandled = false;
if (!hardwareAccelerated) {
mView.invalidateParentCaches();
}
@@ -1152,7 +1158,11 @@ public class ViewPropertyAnimator {
for (int i = 0; i < count; ++i) {
NameValuesHolder values = valueList.get(i);
float value = values.mFromValue + fraction * values.mDeltaValue;
- setValue(values.mNameConstant, value);
+ if (values.mNameConstant == ALPHA) {
+ alphaHandled = mView.setAlphaNoInvalidation(value);
+ } else {
+ setValue(values.mNameConstant, value);
+ }
}
}
if ((propertyMask & TRANSFORM_MASK) != 0) {
@@ -1160,8 +1170,13 @@ public class ViewPropertyAnimator {
mView.mPrivateFlags |= View.PFLAG_DRAWN; // force another invalidation
}
}
-
- mView.invalidateViewProperty(false, false);
+ // invalidate(false) in all cases except if alphaHandled gets set to true
+ // via the call to setAlphaNoInvalidation(), above
+ if (alphaHandled) {
+ mView.invalidate(true);
+ } else {
+ mView.invalidateViewProperty(false, false);
+ }
if (mUpdateListener != null) {
mUpdateListener.onAnimationUpdate(animation);
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 85aba3c3f535..3756a7de47c0 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1997,7 +1997,6 @@ public final class ViewRootImpl implements ViewParent,
mIsInTraversal = true;
mWillDrawSoon = true;
boolean windowSizeMayChange = false;
- final boolean windowAttributesChanged = mWindowAttributesChanged;
WindowManager.LayoutParams lp = mWindowAttributes;
int desiredWindowWidth;
@@ -2014,10 +2013,6 @@ public final class ViewRootImpl implements ViewParent,
((mViewVisibility == View.VISIBLE) != (viewVisibility == View.VISIBLE));
WindowManager.LayoutParams params = null;
- if (mWindowAttributesChanged) {
- mWindowAttributesChanged = false;
- params = lp;
- }
CompatibilityInfo compatibilityInfo =
mDisplay.getDisplayAdjustments().getCompatibilityInfo();
if (compatibilityInfo.supportsScreen() == mLastInCompatMode) {
@@ -2194,16 +2189,6 @@ public final class ViewRootImpl implements ViewParent,
}
}
- if (params != null) {
- if ((host.mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) != 0) {
- if (!PixelFormat.formatHasAlpha(params.format)) {
- params.format = PixelFormat.TRANSLUCENT;
- }
- }
- mAttachInfo.mOverscanRequested = (params.flags
- & WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN) != 0;
- }
-
if (mApplyInsetsRequested) {
mApplyInsetsRequested = false;
mLastOverscanRequested = mAttachInfo.mOverscanRequested;
@@ -2259,6 +2244,21 @@ public final class ViewRootImpl implements ViewParent,
/* True if surface generation id changes. */
boolean surfaceReplaced = false;
+ final boolean windowAttributesChanged = mWindowAttributesChanged;
+ if (windowAttributesChanged) {
+ mWindowAttributesChanged = false;
+ params = lp;
+ }
+
+ if (params != null) {
+ if ((host.mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) != 0
+ && !PixelFormat.formatHasAlpha(params.format)) {
+ params.format = PixelFormat.TRANSLUCENT;
+ }
+ mAttachInfo.mOverscanRequested =
+ (params.flags & WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN) != 0;
+ }
+
if (mFirst || windowShouldResize || insetsChanged ||
viewVisibilityChanged || params != null || mForceNextWindowRelayout) {
mForceNextWindowRelayout = false;
diff --git a/core/java/android/view/WindowlessViewRoot.java b/core/java/android/view/WindowlessViewRoot.java
index 75057a26afae..b76e1fad563e 100644
--- a/core/java/android/view/WindowlessViewRoot.java
+++ b/core/java/android/view/WindowlessViewRoot.java
@@ -21,12 +21,15 @@ import android.content.Context;
import android.view.SurfaceControl;
import android.view.View;
+import android.annotation.TestApi;
+
/**
* Utility class for adding a view hierarchy to a SurfaceControl.
*
* See WindowlessWmTest for example usage.
* @hide
*/
+@TestApi
public class WindowlessViewRoot {
ViewRootImpl mViewRoot;
WindowlessWindowManager mWm;
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 59e7bd236907..430fb6dd9770 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -95,8 +95,9 @@ class WindowlessWindowManager implements IWindowSession {
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) {
+ if (attrs.surfaceInsets != null && attrs.surfaceInsets.left != 0 ||
+ attrs.surfaceInsets.top != 0 || attrs.surfaceInsets.right != 0 ||
+ attrs.surfaceInsets.bottom != 0) {
return false;
}
return !PixelFormat.formatHasAlpha(attrs.format);
@@ -118,9 +119,15 @@ class WindowlessWindowManager implements IWindowSession {
"Invalid window token (never added or removed already)");
}
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- t.show(sc).setBufferSize(sc,
- requestedWidth + attrs.surfaceInsets.left + attrs.surfaceInsets.right,
- requestedHeight + attrs.surfaceInsets.top + attrs.surfaceInsets.bottom)
+
+ final Rect surfaceInsets = attrs.surfaceInsets;
+ int width = surfaceInsets != null ?
+ requestedWidth + surfaceInsets.left + surfaceInsets.right : requestedWidth;
+ int height = surfaceInsets != null ?
+ requestedHeight + surfaceInsets.top + surfaceInsets.bottom : requestedHeight;
+
+ t.show(sc)
+ .setBufferSize(sc, width, height)
.setOpaque(sc, isOpaque(attrs))
.apply();
outSurfaceControl.copyFrom(sc);
diff --git a/core/java/android/view/accessibility/AccessibilityCache.java b/core/java/android/view/accessibility/AccessibilityCache.java
index 484d9a1d173b..dc8bf9b5fbae 100644
--- a/core/java/android/view/accessibility/AccessibilityCache.java
+++ b/core/java/android/view/accessibility/AccessibilityCache.java
@@ -71,7 +71,9 @@ public class AccessibilityCache {
private boolean mIsAllWindowsCached;
- private final SparseArray<AccessibilityWindowInfo> mWindowCache =
+ // The SparseArray of all {@link AccessibilityWindowInfo}s on all displays.
+ // The key of outer SparseArray is display ID and the key of inner SparseArray is window ID.
+ private final SparseArray<SparseArray<AccessibilityWindowInfo>> mWindowCacheByDisplay =
new SparseArray<>();
private final SparseArray<LongSparseArray<AccessibilityNodeInfo>> mNodeCache =
@@ -84,34 +86,66 @@ public class AccessibilityCache {
mAccessibilityNodeRefresher = nodeRefresher;
}
- public void setWindows(List<AccessibilityWindowInfo> windows) {
+ /**
+ * Sets all {@link AccessibilityWindowInfo}s of all displays into the cache.
+ * The key of SparseArray is display ID.
+ *
+ * @param windowsOnAllDisplays The accessibility windows of all displays.
+ */
+ public void setWindowsOnAllDisplays(
+ SparseArray<List<AccessibilityWindowInfo>> windowsOnAllDisplays) {
synchronized (mLock) {
if (DEBUG) {
Log.i(LOG_TAG, "Set windows");
}
- clearWindowCache();
- if (windows == null) {
+ clearWindowCacheLocked();
+ if (windowsOnAllDisplays == null) {
return;
}
- final int windowCount = windows.size();
- for (int i = 0; i < windowCount; i++) {
- final AccessibilityWindowInfo window = windows.get(i);
- addWindow(window);
+
+ final int displayCounts = windowsOnAllDisplays.size();
+ for (int i = 0; i < displayCounts; i++) {
+ final List<AccessibilityWindowInfo> windowsOfDisplay =
+ windowsOnAllDisplays.valueAt(i);
+
+ if (windowsOfDisplay == null) {
+ continue;
+ }
+
+ final int displayId = windowsOnAllDisplays.keyAt(i);
+ final int windowCount = windowsOfDisplay.size();
+ for (int j = 0; j < windowCount; j++) {
+ addWindowByDisplayLocked(displayId, windowsOfDisplay.get(j));
+ }
}
mIsAllWindowsCached = true;
}
}
+ /**
+ * Sets an {@link AccessibilityWindowInfo} into the cache.
+ *
+ * @param window The accessibility window.
+ */
public void addWindow(AccessibilityWindowInfo window) {
synchronized (mLock) {
if (DEBUG) {
- Log.i(LOG_TAG, "Caching window: " + window.getId());
+ Log.i(LOG_TAG, "Caching window: " + window.getId() + " at display Id [ "
+ + window.getDisplayId() + " ]");
}
- final int windowId = window.getId();
- mWindowCache.put(windowId, new AccessibilityWindowInfo(window));
+ addWindowByDisplayLocked(window.getDisplayId(), window);
}
}
+ private void addWindowByDisplayLocked(int displayId, AccessibilityWindowInfo window) {
+ SparseArray<AccessibilityWindowInfo> windows = mWindowCacheByDisplay.get(displayId);
+ if (windows == null) {
+ windows = new SparseArray<>();
+ mWindowCacheByDisplay.put(displayId, windows);
+ }
+ final int windowId = window.getId();
+ windows.put(windowId, new AccessibilityWindowInfo(window));
+ }
/**
* Notifies the cache that the something in the UI changed. As a result
* the cache will either refresh some nodes or evict some nodes.
@@ -236,44 +270,82 @@ public class AccessibilityCache {
}
}
- public List<AccessibilityWindowInfo> getWindows() {
+ /**
+ * Gets all {@link AccessibilityWindowInfo}s of all displays from the cache.
+ *
+ * @return All cached {@link AccessibilityWindowInfo}s of all displays
+ * or null if such not found. The key of SparseArray is display ID.
+ */
+ public SparseArray<List<AccessibilityWindowInfo>> getWindowsOnAllDisplays() {
synchronized (mLock) {
if (!mIsAllWindowsCached) {
return null;
}
+ final SparseArray<List<AccessibilityWindowInfo>> returnWindows = new SparseArray<>();
+ final int displayCounts = mWindowCacheByDisplay.size();
- final int windowCount = mWindowCache.size();
- if (windowCount > 0) {
- // Careful to return the windows in a decreasing layer order.
- SparseArray<AccessibilityWindowInfo> sortedWindows = mTempWindowArray;
- sortedWindows.clear();
+ if (displayCounts > 0) {
+ for (int i = 0; i < displayCounts; i++) {
+ final int displayId = mWindowCacheByDisplay.keyAt(i);
+ final SparseArray<AccessibilityWindowInfo> windowsOfDisplay =
+ mWindowCacheByDisplay.valueAt(i);
- for (int i = 0; i < windowCount; i++) {
- AccessibilityWindowInfo window = mWindowCache.valueAt(i);
- sortedWindows.put(window.getLayer(), window);
- }
+ if (windowsOfDisplay == null) {
+ continue;
+ }
- // It's possible in transient conditions for two windows to share the same
- // layer, which results in sortedWindows being smaller than mWindowCache
- final int sortedWindowCount = sortedWindows.size();
- List<AccessibilityWindowInfo> windows = new ArrayList<>(sortedWindowCount);
- for (int i = sortedWindowCount - 1; i >= 0; i--) {
- AccessibilityWindowInfo window = sortedWindows.valueAt(i);
- windows.add(new AccessibilityWindowInfo(window));
- sortedWindows.removeAt(i);
- }
+ final int windowCount = windowsOfDisplay.size();
+ if (windowCount > 0) {
+ // Careful to return the windows in a decreasing layer order.
+ SparseArray<AccessibilityWindowInfo> sortedWindows = mTempWindowArray;
+ sortedWindows.clear();
- return windows;
+ for (int j = 0; j < windowCount; j++) {
+ AccessibilityWindowInfo window = windowsOfDisplay.valueAt(j);
+ sortedWindows.put(window.getLayer(), window);
+ }
+
+ // It's possible in transient conditions for two windows to share the same
+ // layer, which results in sortedWindows being smaller than
+ // mWindowCacheByDisplay
+ final int sortedWindowCount = sortedWindows.size();
+ List<AccessibilityWindowInfo> windows =
+ new ArrayList<>(sortedWindowCount);
+ for (int j = sortedWindowCount - 1; j >= 0; j--) {
+ AccessibilityWindowInfo window = sortedWindows.valueAt(j);
+ windows.add(new AccessibilityWindowInfo(window));
+ sortedWindows.removeAt(j);
+ }
+ returnWindows.put(displayId, windows);
+ }
+ }
+ return returnWindows;
}
return null;
}
}
+ /**
+ * Gets an {@link AccessibilityWindowInfo} by windowId.
+ *
+ * @param windowId The id of the window.
+ *
+ * @return The {@link AccessibilityWindowInfo} or null if such not found.
+ */
public AccessibilityWindowInfo getWindow(int windowId) {
synchronized (mLock) {
- AccessibilityWindowInfo window = mWindowCache.get(windowId);
- if (window != null) {
- return new AccessibilityWindowInfo(window);
+ final int displayCounts = mWindowCacheByDisplay.size();
+ for (int i = 0; i < displayCounts; i++) {
+ final SparseArray<AccessibilityWindowInfo> windowsOfDisplay =
+ mWindowCacheByDisplay.valueAt(i);
+ if (windowsOfDisplay == null) {
+ continue;
+ }
+
+ AccessibilityWindowInfo window = windowsOfDisplay.get(windowId);
+ if (window != null) {
+ return new AccessibilityWindowInfo(window);
+ }
}
return null;
}
@@ -358,7 +430,7 @@ public class AccessibilityCache {
if (DEBUG) {
Log.i(LOG_TAG, "clear()");
}
- clearWindowCache();
+ clearWindowCacheLocked();
final int nodesForWindowCount = mNodeCache.size();
for (int i = nodesForWindowCount - 1; i >= 0; i--) {
final int windowId = mNodeCache.keyAt(i);
@@ -370,8 +442,23 @@ public class AccessibilityCache {
}
}
- private void clearWindowCache() {
- mWindowCache.clear();
+ private void clearWindowCacheLocked() {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "clearWindowCacheLocked");
+ }
+ final int displayCounts = mWindowCacheByDisplay.size();
+
+ if (displayCounts > 0) {
+ for (int i = displayCounts - 1; i >= 0; i--) {
+ final int displayId = mWindowCacheByDisplay.keyAt(i);
+ final SparseArray<AccessibilityWindowInfo> windows =
+ mWindowCacheByDisplay.get(displayId);
+ if (windows != null) {
+ windows.clear();
+ }
+ mWindowCacheByDisplay.remove(displayId);
+ }
+ }
mIsAllWindowsCached = false;
}
@@ -444,32 +531,41 @@ public class AccessibilityCache {
public void checkIntegrity() {
synchronized (mLock) {
// Get the root.
- if (mWindowCache.size() <= 0 && mNodeCache.size() == 0) {
+ if (mWindowCacheByDisplay.size() <= 0 && mNodeCache.size() == 0) {
return;
}
AccessibilityWindowInfo focusedWindow = null;
AccessibilityWindowInfo activeWindow = null;
- final int windowCount = mWindowCache.size();
- for (int i = 0; i < windowCount; i++) {
- AccessibilityWindowInfo window = mWindowCache.valueAt(i);
+ final int displayCounts = mWindowCacheByDisplay.size();
+ for (int i = 0; i < displayCounts; i++) {
+ final SparseArray<AccessibilityWindowInfo> windowsOfDisplay =
+ mWindowCacheByDisplay.valueAt(i);
- // Check for one active window.
- if (window.isActive()) {
- if (activeWindow != null) {
- Log.e(LOG_TAG, "Duplicate active window:" + window);
- } else {
- activeWindow = window;
- }
+ if (windowsOfDisplay == null) {
+ continue;
}
- // Check for one focused window.
- if (window.isFocused()) {
- if (focusedWindow != null) {
- Log.e(LOG_TAG, "Duplicate focused window:" + window);
- } else {
- focusedWindow = window;
+ final int windowCount = windowsOfDisplay.size();
+ for (int j = 0; j < windowCount; j++) {
+ final AccessibilityWindowInfo window = windowsOfDisplay.valueAt(j);
+
+ // Check for one active window.
+ if (window.isActive()) {
+ if (activeWindow != null) {
+ Log.e(LOG_TAG, "Duplicate active window:" + window);
+ } else {
+ activeWindow = window;
+ }
+ }
+ // Check for one focused window.
+ if (window.isFocused()) {
+ if (focusedWindow != null) {
+ Log.e(LOG_TAG, "Duplicate focused window:" + window);
+ } else {
+ focusedWindow = window;
+ }
}
}
}
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index 4db6f4f808f2..d9fa9f24f1ae 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -28,6 +28,7 @@ import android.os.SystemClock;
import android.util.Log;
import android.util.LongSparseArray;
import android.util.SparseArray;
+import android.view.Display;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
@@ -267,12 +268,14 @@ public final class AccessibilityInteractionClient
try {
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection != null) {
- List<AccessibilityWindowInfo> windows = sAccessibilityCache.getWindows();
- if (windows != null) {
+ SparseArray<List<AccessibilityWindowInfo>> allWindows =
+ sAccessibilityCache.getWindowsOnAllDisplays();
+ List<AccessibilityWindowInfo> windows;
+ if (allWindows != null) {
if (DEBUG) {
Log.i(LOG_TAG, "Windows cache hit");
}
- return windows;
+ return allWindows.valueAt(Display.DEFAULT_DISPLAY);
}
if (DEBUG) {
Log.i(LOG_TAG, "Windows cache miss");
@@ -284,7 +287,9 @@ public final class AccessibilityInteractionClient
Binder.restoreCallingIdentity(identityToken);
}
if (windows != null) {
- sAccessibilityCache.setWindows(windows);
+ allWindows = new SparseArray<>();
+ allWindows.put(Display.DEFAULT_DISPLAY, windows);
+ sAccessibilityCache.setWindowsOnAllDisplays(allWindows);
return windows;
}
} else {
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index 3b60aee8f604..b03732a2a7f0 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -636,9 +636,12 @@ public abstract class Animation implements Cloneable {
*
* @param bg The background color. If 0, no background. Currently must
* be black, with any desired alpha level.
+ *
+ * @deprecated None of window animations are running with background color.
*/
+ @Deprecated
public void setBackgroundColor(@ColorInt int bg) {
- mBackgroundColor = bg;
+ // The background color is not needed any more, do nothing.
}
/**
@@ -665,6 +668,7 @@ public abstract class Animation implements Cloneable {
*
* @deprecated All window animations are running with detached wallpaper.
*/
+ @Deprecated
public void setDetachWallpaper(boolean detachWallpaper) {
}
@@ -793,10 +797,13 @@ public abstract class Animation implements Cloneable {
/**
* Returns the background color behind the animation.
+ *
+ * @deprecated None of window animations are running with background color.
*/
+ @Deprecated
@ColorInt
public int getBackgroundColor() {
- return mBackgroundColor;
+ return 0;
}
/**
@@ -805,6 +812,7 @@ public abstract class Animation implements Cloneable {
*
* @deprecated All window animations are running with detached wallpaper.
*/
+ @Deprecated
public boolean getDetachWallpaper() {
return true;
}
diff --git a/core/java/android/view/contentcapture/ChildContentCaptureSession.java b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
index b3b0b72c8799..5e02de451fa3 100644
--- a/core/java/android/view/contentcapture/ChildContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
@@ -89,6 +89,11 @@ final class ChildContentCaptureSession extends ContentCaptureSession {
}
@Override
+ public void internalNotifySessionLifecycle(boolean started) {
+ getMainCaptureSession().notifySessionLifecycle(mId, started);
+ }
+
+ @Override
boolean isContentCaptureEnabled() {
return getMainCaptureSession().isContentCaptureEnabled();
}
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index 625fcda76563..cf08c18b019a 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -439,6 +439,19 @@ public abstract class ContentCaptureSession implements AutoCloseable {
public abstract void internalNotifyViewTreeEvent(boolean started);
/**
+ * Notifies the Content Capture Service that a session has paused/resumed.
+ *
+ * @param started whether session has resumed.
+ */
+ public final void notifySessionLifecycle(boolean started) {
+ if (!isContentCaptureEnabled()) return;
+
+ internalNotifySessionLifecycle(started);
+ }
+
+ abstract void internalNotifySessionLifecycle(boolean started);
+
+ /**
* Creates a {@link ViewStructure} for a "standard" view.
*
* <p>This method should be called after a visible view is laid out; the view then must populate
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index 1e7440bd5a43..349ef09cf059 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -583,6 +583,11 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
}
@Override
+ public void internalNotifySessionLifecycle(boolean started) {
+ notifySessionLifecycle(mId, started);
+ }
+
+ @Override
boolean isContentCaptureEnabled() {
return super.isContentCaptureEnabled() && mManager.isContentCaptureEnabled();
}
@@ -637,10 +642,9 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
sendEvent(new ContentCaptureEvent(sessionId, type), FORCE_FLUSH);
}
- /** Public because is also used by ViewRootImpl */
- public void notifySessionLifecycle(boolean started) {
+ void notifySessionLifecycle(int sessionId, boolean started) {
final int type = started ? TYPE_SESSION_RESUMED : TYPE_SESSION_PAUSED;
- sendEvent(new ContentCaptureEvent(mId, type), FORCE_FLUSH);
+ sendEvent(new ContentCaptureEvent(sessionId, type), FORCE_FLUSH);
}
void notifyContextUpdated(int sessionId, @Nullable ContentCaptureContext context) {
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index cac75cfd8432..57ce28e5059a 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -2561,7 +2561,8 @@ public class Editor {
* @return True when the TextView isFocused and has a valid zero-length selection (cursor).
*/
private boolean shouldBlink() {
- if (!isCursorVisible() || !mTextView.isFocused()) return false;
+ if (!isCursorVisible() || !mTextView.isFocused() ||
+ !mTextView.isVisibleToUser()) return false;
final int start = mTextView.getSelectionStart();
if (start < 0) return false;
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/ScrollView.java b/core/java/android/widget/ScrollView.java
index eb93fdf5e84a..1cc8ff69408e 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -71,9 +71,9 @@ import java.util.List;
* instead of scroll view which offers greater user interface flexibility and
* support for the material design scrolling patterns.</p>
*
- * <p>To learn more about material design patterns for handling scrolling, see
- * <a href="https://material.io/guidelines/patterns/scrolling-techniques.html#">
- * Scrolling techniques</a>.</p>
+ * <p>Material Design offers guidelines on how the appearance of
+ * <a href="https://material.io/components/">several UI components</a>, including app bars and
+ * banners, should respond to gestures.</p>
*
* @attr ref android.R.styleable#ScrollView_fillViewport
*/
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 38cb2cc1d553..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) {
@@ -1723,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);
@@ -2672,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");
@@ -2682,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;
@@ -2735,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/compat/ChangeReporter.java b/core/java/com/android/internal/compat/ChangeReporter.java
new file mode 100644
index 000000000000..1ce071bd005a
--- /dev/null
+++ b/core/java/com/android/internal/compat/ChangeReporter.java
@@ -0,0 +1,73 @@
+/*
+ * 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.internal.compat;
+
+import android.util.StatsLog;
+
+/**
+ * A helper class to report changes to stats log.
+ *
+ * @hide
+ */
+public final class ChangeReporter {
+
+ /**
+ * Transforms StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE enum to a string.
+ *
+ * @param state to transform
+ * @return a string representing the state
+ */
+ private static String stateToString(int state) {
+ switch (state) {
+ case StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__LOGGED:
+ return "LOGGED";
+ case StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__ENABLED:
+ return "ENABLED";
+ case StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__DISABLED:
+ return "DISABLED";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+ /**
+ * Constructs and returns a string to be logged to logcat when a change is reported.
+ *
+ * @param uid affected by the change
+ * @param changeId the reported change id
+ * @param state of the reported change - enabled/disabled/only logged
+ * @return string to log
+ */
+ public static String createLogString(int uid, long changeId, int state) {
+ return String.format("Compat change id reported: %d; UID %d; state: %s", changeId, uid,
+ stateToString(state));
+ }
+
+ /**
+ * Report the change to stats log.
+ *
+ * @param uid affected by the change
+ * @param changeId the reported change id
+ * @param state of the reported change - enabled/disabled/only logged
+ * @param source of the logging - app process or system server
+ */
+ public void reportChange(int uid, long changeId, int state, int source) {
+ //TODO(b/138374585): Implement rate limiting for stats log.
+ StatsLog.write(StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED, uid, changeId,
+ state, source);
+ }
+}
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..9bddd2aa0f1d 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;
}
}
@@ -13321,7 +13325,8 @@ public class BatteryStatsImpl extends BatteryStats {
return;
}
- if (mBatteryStatsHistory.getActiveFile() == null) {
+ final AtomicFile activeHistoryFile = mBatteryStatsHistory.getActiveFile();
+ if (activeHistoryFile == null) {
Slog.w(TAG,
"readLocked: no history file associated with this instance");
return;
@@ -13332,14 +13337,16 @@ public class BatteryStatsImpl extends BatteryStats {
Parcel stats = Parcel.obtain();
try {
final long start = SystemClock.uptimeMillis();
- byte[] raw = mStatsFile.readFully();
- stats.unmarshall(raw, 0, raw.length);
- stats.setDataPosition(0);
- readSummaryFromParcel(stats);
- if (DEBUG) {
- Slog.d(TAG, "readLocked stats file:" + mStatsFile.getBaseFile().getPath()
- + " bytes:" + raw.length + " takes ms:" + (SystemClock.uptimeMillis()
- - start));
+ if (mStatsFile.exists()) {
+ byte[] raw = mStatsFile.readFully();
+ stats.unmarshall(raw, 0, raw.length);
+ stats.setDataPosition(0);
+ readSummaryFromParcel(stats);
+ if (DEBUG) {
+ Slog.d(TAG, "readLocked stats file:" + mStatsFile.getBaseFile().getPath()
+ + " bytes:" + raw.length + " takes ms:" + (SystemClock.uptimeMillis()
+ - start));
+ }
}
} catch (Exception e) {
Slog.e(TAG, "Error reading battery statistics", e);
@@ -13351,17 +13358,19 @@ public class BatteryStatsImpl extends BatteryStats {
Parcel history = Parcel.obtain();
try {
final long start = SystemClock.uptimeMillis();
- byte[] raw = mBatteryStatsHistory.getActiveFile().readFully();
- if (raw.length > 0) {
- history.unmarshall(raw, 0, raw.length);
- history.setDataPosition(0);
- readHistoryBuffer(history, true);
- }
- if (DEBUG) {
- Slog.d(TAG, "readLocked history file::"
- + mBatteryStatsHistory.getActiveFile().getBaseFile().getPath()
- + " bytes:" + raw.length + " takes ms:" + (SystemClock.uptimeMillis()
- - start));
+ if (activeHistoryFile.exists()) {
+ byte[] raw = activeHistoryFile.readFully();
+ if (raw.length > 0) {
+ history.unmarshall(raw, 0, raw.length);
+ history.setDataPosition(0);
+ readHistoryBuffer(history, true);
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "readLocked history file::"
+ + activeHistoryFile.getBaseFile().getPath()
+ + " bytes:" + raw.length + " takes ms:" + (SystemClock.uptimeMillis()
+ - start));
+ }
}
} catch (Exception e) {
Slog.e(TAG, "Error reading battery history", e);
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/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_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_os_SystemProperties.cpp b/core/jni/android_os_SystemProperties.cpp
index ab5231425b6d..cfdf26abc25c 100644
--- a/core/jni/android_os_SystemProperties.cpp
+++ b/core/jni/android_os_SystemProperties.cpp
@@ -108,7 +108,7 @@ void SystemProperties_set(JNIEnv *env, jobject clazz, jstring keyJ,
if (!ConvertKeyAndForward(env, keyJ, true, handler)) {
// Must have been a failure in SetProperty.
jniThrowException(env, "java/lang/RuntimeException",
- "failed to set system property");
+ "failed to set system property (check logcat for reason)");
}
}
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/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp
index 7975c8675954..c380fd5e439f 100644
--- a/core/jni/android_view_InputEventReceiver.cpp
+++ b/core/jni/android_view_InputEventReceiver.cpp
@@ -83,7 +83,7 @@ private:
return mInputConsumer.getChannel()->getName();
}
- virtual int handleEvent(int receiveFd, int events, void* data);
+ virtual int handleEvent(int receiveFd, int events, void* data) override;
};
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/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index 3323095a6244..b8c5270ef9d8 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2402,6 +2402,10 @@ enum PageId {
// Note: Gear icon is shown next to gesture navigation preference and opens sensitivity dialog
SETTINGS_GESTURE_NAV_BACK_SENSITIVITY_DLG = 1748;
+ // OPEN: Settings > System > Aware > Aware Display
+ // CATEGORY: SETTINGS
+ // OS: Q
+ SETTINGS_AWARE_DISPLAY = 1750;
// ---- End Q Constants, all Q constants go above this line ----
// OPEN: Settings > Network & Internet > Wi-Fi > Click new network
// CATEGORY: SETTINGS
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 777902578e1b..fd1050393de1 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -196,7 +196,7 @@ message StackProto {
repeated TaskProto tasks = 3;
optional bool fills_parent = 4;
optional .android.graphics.RectProto bounds = 5;
- optional bool animation_background_surface_is_dimming = 6;
+ optional bool animation_background_surface_is_dimming = 6 [deprecated=true];
optional bool defer_removal = 7;
optional float minimize_amount = 8;
optional bool adjusted_for_ime = 9;
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/packages/SystemUI/res/drawable/biometric_dialog_bg.xml b/core/res/res/drawable/media_seamless_background.xml
index 0c6d57dd6183..aec89e0e8980 100644
--- a/packages/SystemUI/res/drawable/biometric_dialog_bg.xml
+++ b/core/res/res/drawable/media_seamless_background.xml
@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
-
<!--
- ~ 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.
@@ -15,12 +14,13 @@
~ 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>
+<ripple
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="#1f000000">
+ <item android:id="@android:id/background">
+ <shape android:shape="rectangle">
+ <stroke android:width="1dp" android:color="#1f000000"/>
+ <corners android:radius="20dp"/>
+ </shape>
+ </item>
+</ripple>
diff --git a/core/res/res/layout/notification_material_media_transfer_action.xml b/core/res/res/layout/notification_material_media_transfer_action.xml
new file mode 100644
index 000000000000..98d8f1eee8c9
--- /dev/null
+++ b/core/res/res/layout/notification_material_media_transfer_action.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
+ -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:visibility="gone"
+ android:padding="4dp"
+ android:layout_marginStart="10dp"
+ android:gravity="center"
+ android:background="@drawable/media_seamless_background">
+ <ImageView
+ android:layout_width="?attr/notificationHeaderIconSize"
+ android:layout_height="?attr/notificationHeaderIconSize"
+ android:src="@drawable/ic_media_seamless"
+ android:id="@+id/media_seamless_image" />
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?attr/notificationHeaderTextAppearance"
+ android:text="@string/ext_media_seamless_action"
+ android:id="@+id/media_seamless_text"
+ android:paddingEnd="2dp" />
+</LinearLayout> \ No newline at end of file
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index 0f53549a966c..f5fa1b6a795a 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -175,5 +175,9 @@
android:contentDescription="@string/notification_appops_overlay_active"
/>
</LinearLayout>
+ <include
+ layout="@layout/notification_material_media_transfer_action"
+ android:id="@+id/media_seamless"
+ />
</NotificationHeaderView>
diff --git a/core/res/res/layout/notification_template_material_big_media.xml b/core/res/res/layout/notification_template_material_big_media.xml
index 3267f726c69d..6c47c2c51601 100644
--- a/core/res/res/layout/notification_template_material_big_media.xml
+++ b/core/res/res/layout/notification_template_material_big_media.xml
@@ -80,10 +80,6 @@
layout="@layout/notification_material_media_action"
android:id="@+id/action4"
/>
- <include
- layout="@layout/notification_material_media_action"
- android:id="@+id/media_seamless"
- />
</LinearLayout>
<ViewStub android:id="@+id/notification_media_seekbar_container"
android:layout_width="match_parent"
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 9ba0481b1940..a44b137ab93d 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Ongekategoriseer"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Jy stel die belangrikheid van hierdie kennisgewings."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Dit is belangrik as gevolg van die mense wat betrokke is."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Laat <xliff:g id="APP">%1$s</xliff:g> toe om \'n nuwe gebruiker met <xliff:g id="ACCOUNT">%2$s</xliff:g> te skep?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Laat <xliff:g id="APP">%1$s</xliff:g> toe om \'n nuwe gebruiker met <xliff:g id="ACCOUNT">%2$s</xliff:g> te skep (\'n gebruiker met hierdie rekening bestaan reeds)?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Voeg \'n taal by"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Streekvoorkeur"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Voer taalnaam in"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 013537c31c50..db758e3e45f8 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"ያልተመደቡ"</string>
<string name="importance_from_user" msgid="7318955817386549931">"የእነዚህን ማሳወቂያዎች አስፈላጊነት አዘጋጅተዋል።"</string>
<string name="importance_from_person" msgid="9160133597262938296">"ይሄ በሚሳተፉ ሰዎች ምክንያት አስፈላጊ ነው።"</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"<xliff:g id="APP">%1$s</xliff:g> በ<xliff:g id="ACCOUNT">%2$s</xliff:g> አዲስ ተጠቃሚ እንዲፈጥር ይፈቀድለት?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"<xliff:g id="APP">%1$s</xliff:g> በ<xliff:g id="ACCOUNT">%2$s</xliff:g> አዲስ ተጠቃሚ እንዲፈጥር ይፈቀድለት (ይህ መለያ ያለው ተጠቃሚ አስቀድሞ አለ)?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"ቋንቋ ያክሉ"</string>
<string name="country_selection_title" msgid="2954859441620215513">"የክልል ምርጫ"</string>
<string name="search_language_hint" msgid="7042102592055108574">"የቋንቋ ስም ይተይቡ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 2bef980fd8d8..113a3426cf5f 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -474,7 +474,7 @@
<string name="permdesc_transmitIr" product="tablet" msgid="5358308854306529170">"للسماح للتطبيق باستخدام مرسل الأشعة تحت الحمراء الخاص بالجهاز اللوحي."</string>
<string name="permdesc_transmitIr" product="tv" msgid="2752076865253892198">"‏للسماح للتطبيق باستخدام مُرسِل الأشعة تحت الحمراء في جهاز Android TV."</string>
<string name="permdesc_transmitIr" product="default" msgid="7957763745020300725">"للسماح للتطبيق باستخدام مرسل الأشعة تحت الحمراء الخاص بالهاتف."</string>
- <string name="permlab_setWallpaper" msgid="6627192333373465143">"تعيين الخلفية"</string>
+ <string name="permlab_setWallpaper" msgid="6627192333373465143">"ضبط الخلفية"</string>
<string name="permdesc_setWallpaper" msgid="7373447920977624745">"للسماح للتطبيق بتعيين خلفية النظام."</string>
<string name="permlab_setWallpaperHints" msgid="3278608165977736538">"تعديل حجم الخلفية"</string>
<string name="permdesc_setWallpaperHints" msgid="8235784384223730091">"للسماح للتطبيق بتعيين تلميحات حجم خلفية النظام."</string>
@@ -1346,7 +1346,7 @@
<string name="wifi_available_action_connect" msgid="2635699628459488788">"اتصال"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"جميع الشبكات"</string>
<string name="wifi_suggestion_title" msgid="6396033039578436801">"‏هل تريد السماح لشبكات Wi‑Fi المقترحة؟"</string>
- <string name="wifi_suggestion_content" msgid="5603992011371520746">"<xliff:g id="NAME">%s</xliff:g> الشبكات المقترحة. قد يتم توصيل الجهاز تلقائيًا."</string>
+ <string name="wifi_suggestion_content" msgid="5603992011371520746">"شبكات <xliff:g id="NAME">%s</xliff:g> المقترحة - قد يتم توصيل الجهاز تلقائيًا."</string>
<string name="wifi_suggestion_action_allow_app" msgid="7978995387498669901">"سماح"</string>
<string name="wifi_suggestion_action_disallow_app" msgid="6434097275967940372">"لا، شكرًا"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"‏سيتم تشغيل شبكة Wi-Fi تلقائيًا."</string>
@@ -2026,8 +2026,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"غير مصنفة"</string>
<string name="importance_from_user" msgid="7318955817386549931">"لقد عيَّنت أهمية هذه الإشعارات."</string>
<string name="importance_from_person" msgid="9160133597262938296">"هذه الرسالة مهمة نظرًا لأهمية الأشخاص المعنيين."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"هل تسمح لـ <xliff:g id="APP">%1$s</xliff:g> بإنشاء مستخدم جديد باستخدام <xliff:g id="ACCOUNT">%2$s</xliff:g>؟"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"هل تسمح لـ <xliff:g id="APP">%1$s</xliff:g> بإنشاء مستخدم جديد باستخدام <xliff:g id="ACCOUNT">%2$s</xliff:g> (يوجد مستخدم بهذا الحساب مسبقًا)؟"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"إضافة لغة"</string>
<string name="country_selection_title" msgid="2954859441620215513">"تفضيل المنطقة"</string>
<string name="search_language_hint" msgid="7042102592055108574">"اكتب اسم اللغة"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 446575b56a70..00617a548082 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"শ্ৰেণীবদ্ধ নকৰা"</string>
<string name="importance_from_user" msgid="7318955817386549931">"এই জাননীবোৰৰ গুৰুত্ব আপুনি ছেট কৰব লাগিব।"</string>
<string name="importance_from_person" msgid="9160133597262938296">"এই কার্যৰ সৈতে জড়িত থকা লোকসকলক ভিত্তি কৰি এইয়া গুৰুত্বপূর্ণ বুলি বিবেচনা কৰা হৈছ।"</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"<xliff:g id="APP">%1$s</xliff:g> ক <xliff:g id="ACCOUNT">%2$s</xliff:g>ৰ জৰিয়তে নতুন ব্য়ৱহাৰকাৰী সৃষ্টি কৰিবলৈ অনুমতি দিবনে?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"<xliff:g id="APP">%1$s</xliff:g>ক <xliff:g id="ACCOUNT">%2$s</xliff:g>ৰ (এই একাউন্টৰ এজন ব্য়ৱহাৰকাৰী ইতিমধ্যে আছে) জৰিয়তে নতুন ব্য়ৱহাৰকাৰী সৃষ্টি কৰিবলৈ অনুমতি দিবনে?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"ভাষা যোগ কৰক"</string>
<string name="country_selection_title" msgid="2954859441620215513">"অঞ্চলৰ অগ্ৰাধিকাৰ"</string>
<string name="search_language_hint" msgid="7042102592055108574">"ভাষাৰ নাম লিখক"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 6e93a970735d..f79decd068d3 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Kateqoriyasız"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Bildirişlərin əhəmiyyətini Siz ayarlaryırsınız."</string>
<string name="importance_from_person" msgid="9160133597262938296">"İnsanlar cəlb olunduğu üçün bu vacibdir."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"<xliff:g id="APP">%1$s</xliff:g> tətbiqinə <xliff:g id="ACCOUNT">%2$s</xliff:g> hesabı ilə yeni İstifadəçi yaratmağa icazə verilsin?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"<xliff:g id="APP">%1$s</xliff:g> tətbiqinə<xliff:g id="ACCOUNT">%2$s</xliff:g> (bu hesab ilə İstifadəçi artıq mövcuddur) hesabı ilə yeni İstifadəçi yaratmağa icazə verilsin?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Dil əlavə edin"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Region seçimi"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Dil adını daxil edin"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index e14139c0ab13..10dc339ae8ff 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -1924,8 +1924,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Nekategorizovano"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Vi podešavate važnost ovih obaveštenja."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Ovo je važno zbog ljudi koji učestvuju."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Želite li da dozvolite aplikaciji <xliff:g id="APP">%1$s</xliff:g> da napravi novog korisnika za <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Želite li da dozvolite aplikaciji <xliff:g id="APP">%1$s</xliff:g> da napravi novog korisnika za <xliff:g id="ACCOUNT">%2$s</xliff:g> (korisnik sa ovim nalogom već postoji)?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Dodajte jezik"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Podešavanje regiona"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Unesite naziv jezika"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index a71eef0159e8..ce65bead0fd1 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -1302,7 +1302,7 @@
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Падключыцца"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Усе сеткі"</string>
<string name="wifi_suggestion_title" msgid="6396033039578436801">"Дазволіць падключэнне да прапанаваных сетак Wi‑Fi?"</string>
- <string name="wifi_suggestion_content" msgid="5603992011371520746">"Праграма \"<xliff:g id="NAME">%s</xliff:g>\" прапанавала сеткі. Прылада можа падключыцца аўтаматычна."</string>
+ <string name="wifi_suggestion_content" msgid="5603992011371520746">"Праграма \"<xliff:g id="NAME">%s</xliff:g>\" прапанавала сеткі. Прылада можа падключыцца да ніх аўтаматычна."</string>
<string name="wifi_suggestion_action_allow_app" msgid="7978995387498669901">"Дазволіць"</string>
<string name="wifi_suggestion_action_disallow_app" msgid="6434097275967940372">"Не, дзякуй"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi уключыцца аўтаматычна"</string>
@@ -1389,7 +1389,7 @@
<string name="no_permissions" msgid="7283357728219338112">"Дазволу не патрабуецца"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"за гэта можа спаганяцца плата"</string>
<string name="dlg_ok" msgid="7376953167039865701">"ОК"</string>
- <string name="usb_charging_notification_title" msgid="1595122345358177163">"Зарадка гэтай прылады праз USB"</string>
+ <string name="usb_charging_notification_title" msgid="1595122345358177163">"Прылада зараджаецца праз USB"</string>
<string name="usb_supplying_notification_title" msgid="4631045789893086181">"Зарадка падключанай прылады праз USB"</string>
<string name="usb_mtp_notification_title" msgid="4238227258391151029">"Перадача файлаў праз USB"</string>
<string name="usb_ptp_notification_title" msgid="5425857879922006878">"Перадача фота (PTP) праз USB"</string>
@@ -1720,8 +1720,8 @@
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> быў адключаны з дапамогай камбінацыі хуткага доступу для спецыяльных магчымасцей"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Каб карыстацца сэрвісам \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\", націсніце і ўтрымлівайце на працягу трох секунд абедзве клавішы гучнасці"</string>
<string name="accessibility_button_prompt_text" msgid="1176658502969738564">"Выберыце службу для выкарыстання пры націску кнопкі \"Спецыяльныя магчымасці\":"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8259145549733019401">"З дапамогай жэста спецыяльных магчымасцей (правесці двума пальцамі па экране знізу ўверх) выберыце службу для выкарыстання:"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="1041435574275047665">"З дапамогай жэста спецыяльных магчымасцей (правесці трыма пальцамі па экране знізу ўверх) выберыце службу для выкарыстання:"</string>
+ <string name="accessibility_gesture_prompt_text" msgid="8259145549733019401">"Выберыце службу, дзе будзе выкарыстоўвацца жэст спецыяльных магчымасцей (правесці двума пальцамі па экране знізу ўверх):"</string>
+ <string name="accessibility_gesture_3finger_prompt_text" msgid="1041435574275047665">"Выберыце службу, дзе будзе выкарыстоўвацца жэст спецыяльных магчымасцей (правесці двума пальцамі па экране знізу ўверх):"</string>
<string name="accessibility_button_instructional_text" msgid="7003212763213614833">"Каб пераключыцца на другую службу, націсніце і ўтрымлівайце кнопку спецыяльных магчымасцей."</string>
<string name="accessibility_gesture_instructional_text" msgid="5261788874937410950">"Каб пераключыцца на другую службу, правядзіце ўверх двума пальцамі, утрымліваючы іх на экране."</string>
<string name="accessibility_gesture_3finger_instructional_text" msgid="4969448938984394550">"Каб пераключыцца на іншую службу, правядзіце ўверх трыма пальцамі, утрымліваючы іх на экране."</string>
@@ -1958,8 +1958,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Некатэгарызаванае"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Вы задалі важнасць гэтых апавяшчэнняў."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Гэта важна, бо з гэтым звязаны пэўныя людзі."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Дазволіць <xliff:g id="APP">%1$s</xliff:g> стварыць новага Карыстальніка з уліковым запісам <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Дазволіць <xliff:g id="APP">%1$s</xliff:g> стварыць новага Карыстальніка з уліковым запісам <xliff:g id="ACCOUNT">%2$s</xliff:g> (Карыстальнік з гэтым уліковым запісам ужо існуе)?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Дадаць мову"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Параметры рэгіёна"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Увядзіце назву мовы"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 2c6550dd1466..87ea6de20162 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Некатегоризирани"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Зададохте важността на тези известия."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Това е важно заради участващите хора."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Да се разреши ли на <xliff:g id="APP">%1$s</xliff:g> да създаде нов потребител с профила <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Да се разреши ли на <xliff:g id="APP">%1$s</xliff:g> да създаде нов потребител с профила <xliff:g id="ACCOUNT">%2$s</xliff:g> (вече съществува потребител с този профил)?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Добавяне на език"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Предпочитание за региона"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Въведете име на език"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 7f5a3cca6f22..c0843eaeba7d 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -1891,8 +1891,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"বিভাগ নির্ধারিত নয়"</string>
<string name="importance_from_user" msgid="7318955817386549931">"আপনি এই বিজ্ঞপ্তিগুলির গুরুত্ব সেট করেছেন।"</string>
<string name="importance_from_person" msgid="9160133597262938296">"লোকজন জড়িত থাকার কারণে এটি গুরুত্বপূর্ণ।"</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"<xliff:g id="APP">%1$s</xliff:g> কে <xliff:g id="ACCOUNT">%2$s</xliff:g> এর সাথে একজন নতুন ব্যবহারকারী তৈরি করার অনুমতি দেবেন কি?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"<xliff:g id="APP">%1$s</xliff:g> কে <xliff:g id="ACCOUNT">%2$s</xliff:g> (একজন ব্যবহারকারী এই অ্যাকাউন্টে ইতিমধ্যেই বিদ্যমান আছেন) এর সাথে একজন নতুন ব্যবহারকারী তৈরি করার অনুমতি দেবেন কি?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"একটি ভাষা যোগ করুন"</string>
<string name="country_selection_title" msgid="2954859441620215513">"পছন্দের অঞ্চল"</string>
<string name="search_language_hint" msgid="7042102592055108574">"ভাষার নাম লিখুন"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index bc84e938754b..0d4b12c30332 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -295,7 +295,7 @@
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da pristupi vašem kalendaru?"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
<string name="permgroupdesc_sms" msgid="4656988620100940350">"šalje i pregleda SMS poruke"</string>
- <string name="permgrouprequest_sms" msgid="7168124215838204719">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; slanje i pregled SMS poruka?"</string>
+ <string name="permgrouprequest_sms" msgid="7168124215838204719">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da šalje i pregleda SMS poruke?"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"Pohrana"</string>
<string name="permgroupdesc_storage" msgid="637758554581589203">"pristupa slikama, medijskim fajlovima i fajlovima na vašem uređaju"</string>
<string name="permgrouprequest_storage" msgid="7885942926944299560">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da pristupa fotografijama, medijima i fajlovima na vašem uređaju?"</string>
@@ -1380,7 +1380,7 @@
<string name="usb_power_notification_message" msgid="4647527153291917218">"Punjenje povezanog uređaja. Dodirnite za više opcija."</string>
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"Otkriven je analogni periferni uređaj"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"Priključeni uređaj nije kompatibilan s ovim telefonom. Dodirnite da saznate više."</string>
- <string name="adb_active_notification_title" msgid="6729044778949189918">"Otklanjanje grešaka putem uređaja spojenog na USB je uspostavljeno"</string>
+ <string name="adb_active_notification_title" msgid="6729044778949189918">"Otklanjanje grešaka putem USB-a je uspostavljeno"</string>
<string name="adb_active_notification_message" msgid="7463062450474107752">"Dodirnite da isključite otklanjanje grešaka putem USB-a"</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Odaberite da onemogućite ispravljanje grešaka koristeći USB"</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Omogućen način rada okvira za testiranje"</string>
@@ -1926,8 +1926,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Nije kategorizirano"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Vi određujete značaj ovih obavještenja."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Ovo je značajno zbog osoba koje su uključene."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Da li dozvoljavate aplikaciji <xliff:g id="APP">%1$s</xliff:g> da kreira novog korisnika s računom <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Da li dozvoljavate da <xliff:g id="APP">%1$s</xliff:g> kreira novog korisnika za <xliff:g id="ACCOUNT">%2$s</xliff:g> (Korisnik sa ovim nalogom već postoji)?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Dodajte jezik"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Izbor regije"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Upišite ime jezika"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 8334eca0d874..d82884434305 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Sense classificar"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Has definit la importància d\'aquestes notificacions."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Aquest missatge és important per les persones implicades."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Concedeixes permís a <xliff:g id="APP">%1$s</xliff:g> per crear un usuari amb el compte <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Concedeixes permís a <xliff:g id="APP">%1$s</xliff:g> per crear un usuari amb el compte <xliff:g id="ACCOUNT">%2$s</xliff:g>? (Ja hi ha un usuari amb aquest compte.)"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Afegeix un idioma"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Preferència de regió"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Nom de l\'idioma"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 0d057097e04d..2b524ae64aba 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1958,8 +1958,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Neklasifikováno"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Důležitost oznámení určujete vy."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Tato zpráva je důležitá kvůli lidem zapojeným do konverzace."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Povolit aplikaci <xliff:g id="APP">%1$s</xliff:g> vytvořit nového uživatele s účtem <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Povolit aplikaci <xliff:g id="APP">%1$s</xliff:g> vytvořit nového uživatele s účtem <xliff:g id="ACCOUNT">%2$s</xliff:g>? (Uživatel s tímto účtem již existuje.)"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Přidat jazyk"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Preferovaná oblast"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Zadejte název jazyka"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 70a464644153..4d57a90fa068 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Uden kategori"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Du angiver, hvor vigtige disse notifikationer er."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Dette er vigtigt på grund af de personer, det handler om."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Vil du give <xliff:g id="APP">%1$s</xliff:g> tilladelse til at oprette en ny bruger med <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Vil du give <xliff:g id="APP">%1$s</xliff:g> tilladelse til at oprette en ny bruger med <xliff:g id="ACCOUNT">%2$s</xliff:g> (der findes allerede en bruger med denne konto)?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Tilføj et sprog"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Områdeindstilling"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Angiv sprog"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index c7e381c73903..2ec70d36a412 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -283,7 +283,7 @@
<string name="permgrouprequest_contacts" msgid="6032805601881764300">"Zulassen, dass &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; auf deine Kontakte zugreift?"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Standort"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"auf den Standort deines Geräts zugreifen"</string>
- <string name="permgrouprequest_location" msgid="3788275734953323491">"Zulassen, dass &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; den Gerätestandort abruft?"</string>
+ <string name="permgrouprequest_location" msgid="3788275734953323491">"Zulassen, dass die App &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; den Gerätestandort abruft?"</string>
<string name="permgrouprequestdetail_location" msgid="1347189607421252902">"Die App hat nur Zugriff auf den Gerätestandort, solange du sie verwendest"</string>
<string name="permgroupbackgroundrequest_location" msgid="5039063878675613235">"Zulassen, dass &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; &lt;b&gt;ständig&lt;/b&gt; auf deinen Standort zugreift?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="4597006851453417387">"Die App hat gegenwärtig nur dann Zugriff auf den Gerätestandort, wenn du sie verwendest"</string>
@@ -292,7 +292,7 @@
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Zulassen, dass &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; auf deinen Kalender zugreift?"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
<string name="permgroupdesc_sms" msgid="4656988620100940350">"SMS senden und abrufen"</string>
- <string name="permgrouprequest_sms" msgid="7168124215838204719">"Zulassen, dass &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; SMS sendet und aufruft?"</string>
+ <string name="permgrouprequest_sms" msgid="7168124215838204719">"Zulassen, dass die App &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; SMS sendet und aufruft?"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"Speicher"</string>
<string name="permgroupdesc_storage" msgid="637758554581589203">"auf Fotos, Medien und Dateien auf deinem Gerät zugreifen"</string>
<string name="permgrouprequest_storage" msgid="7885942926944299560">"Zulassen, dass &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; auf Fotos, Medien und Dateien auf deinem Gerät zugreift?"</string>
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Unkategorisiert"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Du hast die Wichtigkeit dieser Benachrichtigungen festgelegt."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Diese Benachrichtigung ist aufgrund der beteiligten Personen wichtig."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Möchtest du zulassen, dass <xliff:g id="APP">%1$s</xliff:g> einen neuen Nutzer mit <xliff:g id="ACCOUNT">%2$s</xliff:g> erstellt?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Möchtest du zulassen, dass <xliff:g id="APP">%1$s</xliff:g> einen neuen Nutzer mit <xliff:g id="ACCOUNT">%2$s</xliff:g> erstellt? Dieses Konto wird jedoch bereits von einem anderen Nutzer verwendet."</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Sprache hinzufügen"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Region auswählen"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Sprache eingeben"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 18af40d6acbe..6998c0c16811 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1675,7 +1675,7 @@
<string name="accessibility_gesture_prompt_text" msgid="8259145549733019401">"Επιλέξτε μια υπηρεσία που θα χρησιμοποιείται με την κίνηση προσβασιμότητας (σύρετε με δύο δάχτυλα προς τα επάνω από το κάτω μέρος της οθόνης):"</string>
<string name="accessibility_gesture_3finger_prompt_text" msgid="1041435574275047665">"Επιλέξτε μια υπηρεσία που θα χρησιμοποιείται με την κίνηση προσβασιμότητας (σύρετε με τρία δάχτυλα προς τα επάνω από το κάτω μέρος της οθόνης):"</string>
<string name="accessibility_button_instructional_text" msgid="7003212763213614833">"Για εναλλαγή μεταξύ υπηρεσιών, αγγίξτε παρατεταμένα το κουμπί προσβασιμότητας."</string>
- <string name="accessibility_gesture_instructional_text" msgid="5261788874937410950">"Για εναλλαγή μεταξύ υπηρεσιών, σύρετε παρατεταμένα με δύο δάχτυλα προς τα επάνω."</string>
+ <string name="accessibility_gesture_instructional_text" msgid="5261788874937410950">"Για εναλλαγή μεταξύ υπηρεσιών, σύρετε παρατεταμένα με δύο δάχτυλα προς τα επάνω και μην τα απομακρύνετε από την οθόνη."</string>
<string name="accessibility_gesture_3finger_instructional_text" msgid="4969448938984394550">"Για εναλλαγή μεταξύ υπηρεσιών, σύρετε παρατεταμένα με τρία δάχτυλα προς τα επάνω."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Μεγιστοποίηση"</string>
<string name="user_switched" msgid="3768006783166984410">"Τρέχων χρήστης <xliff:g id="NAME">%1$s</xliff:g>."</string>
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Μη κατηγοριοποιημένο"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Μπορείτε να ρυθμίσετε τη βαρύτητα αυτών των ειδοποιήσεων."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Αυτό είναι σημαντικό λόγω των ατόμων που συμμετέχουν."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Να επιτραπεί στην εφαρμογή <xliff:g id="APP">%1$s</xliff:g> να δημιουργήσει έναν νέο χρήστη με το λογαριασμό <xliff:g id="ACCOUNT">%2$s</xliff:g>;"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Να επιτραπεί στην εφαρμογή <xliff:g id="APP">%1$s</xliff:g> να δημιουργήσει έναν νέο χρήστη με το λογαριασμό <xliff:g id="ACCOUNT">%2$s</xliff:g> (υπάρχει ήδη χρήστης με αυτόν το λογαριασμό);"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Προσθήκη γλώσσας"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Προτίμηση περιοχής"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Εισαγ. όνομα γλώσσας"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 2db377157c24..7c2b32989c5d 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -554,10 +554,10 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Fingerprint icon"</string>
- <string name="permlab_manageFace" msgid="7262837876352591553">"manage Face Unlock hardware"</string>
+ <string name="permlab_manageFace" msgid="7262837876352591553">"manage face unlock hardware"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Allows the app to invoke methods to add and delete facial templates for use."</string>
- <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"use Face Unlock hardware"</string>
- <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Allows the app to use Face Unlock hardware for authentication"</string>
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"use face unlock hardware"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Allows the app to use face unlock hardware for authentication"</string>
<string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Face unlock"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Re-enrol your face"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"To improve recognition, please re-enrol your face"</string>
@@ -584,15 +584,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Can’t verify face. Hardware not available."</string>
- <string name="face_error_timeout" msgid="981512090365729465">"Try Face Unlock again."</string>
+ <string name="face_error_timeout" msgid="981512090365729465">"Try face unlock again."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Can’t store new face data. Delete an old one first."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Face operation cancelled."</string>
- <string name="face_error_user_canceled" msgid="5317030072349668946">"Face Unlock cancelled by user."</string>
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"Face unlock cancelled by user."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Too many attempts. Try again later."</string>
- <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Too many attempts. Face Unlock disabled."</string>
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Too many attempts. Face unlock disabled."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Can’t verify face. Try again."</string>
- <string name="face_error_not_enrolled" msgid="4016937174832839540">"You haven’t set up Face Unlock."</string>
- <string name="face_error_hw_not_present" msgid="8302690289757559738">"Face Unlock is not supported on this device."</string>
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"You haven’t set up face unlock."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"Face unlock is not supported on this device."</string>
<string name="face_name_template" msgid="7004562145809595384">"Face <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -821,7 +821,7 @@
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Try again"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Try again"</string>
<string name="lockscreen_storage_locked" msgid="9167551160010625200">"Unlock for all features and data"</string>
- <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Maximum Face Unlock attempts exceeded"</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Maximum face unlock attempts exceeded"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"No SIM card"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"No SIM card in tablet."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="3360993527792167595">"No SIM card in your Android TV device."</string>
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Uncategorised"</string>
<string name="importance_from_user" msgid="7318955817386549931">"You set the importance of these notifications."</string>
<string name="importance_from_person" msgid="9160133597262938296">"This is important because of the people involved."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Allow <xliff:g id="APP">%1$s</xliff:g> to create a new User with <xliff:g id="ACCOUNT">%2$s</xliff:g> ?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Allow <xliff:g id="APP">%1$s</xliff:g> to create a new User with <xliff:g id="ACCOUNT">%2$s</xliff:g> (a User with this account already exists) ?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Add a language"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Region preference"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Type language name"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 839aaac16c7a..6da3a5fbdcf9 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -554,10 +554,10 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Fingerprint icon"</string>
- <string name="permlab_manageFace" msgid="7262837876352591553">"manage Face Unlock hardware"</string>
+ <string name="permlab_manageFace" msgid="7262837876352591553">"manage face unlock hardware"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Allows the app to invoke methods to add and delete facial templates for use."</string>
- <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"use Face Unlock hardware"</string>
- <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Allows the app to use Face Unlock hardware for authentication"</string>
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"use face unlock hardware"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Allows the app to use face unlock hardware for authentication"</string>
<string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Face unlock"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Re-enrol your face"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"To improve recognition, please re-enrol your face"</string>
@@ -584,15 +584,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Can’t verify face. Hardware not available."</string>
- <string name="face_error_timeout" msgid="981512090365729465">"Try Face Unlock again."</string>
+ <string name="face_error_timeout" msgid="981512090365729465">"Try face unlock again."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Can’t store new face data. Delete an old one first."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Face operation cancelled."</string>
- <string name="face_error_user_canceled" msgid="5317030072349668946">"Face Unlock cancelled by user."</string>
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"Face unlock cancelled by user."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Too many attempts. Try again later."</string>
- <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Too many attempts. Face Unlock disabled."</string>
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Too many attempts. Face unlock disabled."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Can’t verify face. Try again."</string>
- <string name="face_error_not_enrolled" msgid="4016937174832839540">"You haven’t set up Face Unlock."</string>
- <string name="face_error_hw_not_present" msgid="8302690289757559738">"Face Unlock is not supported on this device."</string>
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"You haven’t set up face unlock."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"Face unlock is not supported on this device."</string>
<string name="face_name_template" msgid="7004562145809595384">"Face <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -821,7 +821,7 @@
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Try again"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Try again"</string>
<string name="lockscreen_storage_locked" msgid="9167551160010625200">"Unlock for all features and data"</string>
- <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Maximum Face Unlock attempts exceeded"</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Maximum face unlock attempts exceeded"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"No SIM card"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"No SIM card in tablet."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="3360993527792167595">"No SIM card in your Android TV device."</string>
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Uncategorised"</string>
<string name="importance_from_user" msgid="7318955817386549931">"You set the importance of these notifications."</string>
<string name="importance_from_person" msgid="9160133597262938296">"This is important because of the people involved."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Allow <xliff:g id="APP">%1$s</xliff:g> to create a new User with <xliff:g id="ACCOUNT">%2$s</xliff:g> ?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Allow <xliff:g id="APP">%1$s</xliff:g> to create a new User with <xliff:g id="ACCOUNT">%2$s</xliff:g> (a User with this account already exists) ?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Add a language"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Region preference"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Type language name"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 2db377157c24..7c2b32989c5d 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -554,10 +554,10 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Fingerprint icon"</string>
- <string name="permlab_manageFace" msgid="7262837876352591553">"manage Face Unlock hardware"</string>
+ <string name="permlab_manageFace" msgid="7262837876352591553">"manage face unlock hardware"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Allows the app to invoke methods to add and delete facial templates for use."</string>
- <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"use Face Unlock hardware"</string>
- <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Allows the app to use Face Unlock hardware for authentication"</string>
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"use face unlock hardware"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Allows the app to use face unlock hardware for authentication"</string>
<string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Face unlock"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Re-enrol your face"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"To improve recognition, please re-enrol your face"</string>
@@ -584,15 +584,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Can’t verify face. Hardware not available."</string>
- <string name="face_error_timeout" msgid="981512090365729465">"Try Face Unlock again."</string>
+ <string name="face_error_timeout" msgid="981512090365729465">"Try face unlock again."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Can’t store new face data. Delete an old one first."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Face operation cancelled."</string>
- <string name="face_error_user_canceled" msgid="5317030072349668946">"Face Unlock cancelled by user."</string>
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"Face unlock cancelled by user."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Too many attempts. Try again later."</string>
- <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Too many attempts. Face Unlock disabled."</string>
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Too many attempts. Face unlock disabled."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Can’t verify face. Try again."</string>
- <string name="face_error_not_enrolled" msgid="4016937174832839540">"You haven’t set up Face Unlock."</string>
- <string name="face_error_hw_not_present" msgid="8302690289757559738">"Face Unlock is not supported on this device."</string>
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"You haven’t set up face unlock."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"Face unlock is not supported on this device."</string>
<string name="face_name_template" msgid="7004562145809595384">"Face <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -821,7 +821,7 @@
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Try again"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Try again"</string>
<string name="lockscreen_storage_locked" msgid="9167551160010625200">"Unlock for all features and data"</string>
- <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Maximum Face Unlock attempts exceeded"</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Maximum face unlock attempts exceeded"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"No SIM card"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"No SIM card in tablet."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="3360993527792167595">"No SIM card in your Android TV device."</string>
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Uncategorised"</string>
<string name="importance_from_user" msgid="7318955817386549931">"You set the importance of these notifications."</string>
<string name="importance_from_person" msgid="9160133597262938296">"This is important because of the people involved."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Allow <xliff:g id="APP">%1$s</xliff:g> to create a new User with <xliff:g id="ACCOUNT">%2$s</xliff:g> ?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Allow <xliff:g id="APP">%1$s</xliff:g> to create a new User with <xliff:g id="ACCOUNT">%2$s</xliff:g> (a User with this account already exists) ?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Add a language"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Region preference"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Type language name"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 2db377157c24..7c2b32989c5d 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -554,10 +554,10 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Fingerprint icon"</string>
- <string name="permlab_manageFace" msgid="7262837876352591553">"manage Face Unlock hardware"</string>
+ <string name="permlab_manageFace" msgid="7262837876352591553">"manage face unlock hardware"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Allows the app to invoke methods to add and delete facial templates for use."</string>
- <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"use Face Unlock hardware"</string>
- <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Allows the app to use Face Unlock hardware for authentication"</string>
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"use face unlock hardware"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Allows the app to use face unlock hardware for authentication"</string>
<string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Face unlock"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Re-enrol your face"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"To improve recognition, please re-enrol your face"</string>
@@ -584,15 +584,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Can’t verify face. Hardware not available."</string>
- <string name="face_error_timeout" msgid="981512090365729465">"Try Face Unlock again."</string>
+ <string name="face_error_timeout" msgid="981512090365729465">"Try face unlock again."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Can’t store new face data. Delete an old one first."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Face operation cancelled."</string>
- <string name="face_error_user_canceled" msgid="5317030072349668946">"Face Unlock cancelled by user."</string>
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"Face unlock cancelled by user."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Too many attempts. Try again later."</string>
- <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Too many attempts. Face Unlock disabled."</string>
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Too many attempts. Face unlock disabled."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Can’t verify face. Try again."</string>
- <string name="face_error_not_enrolled" msgid="4016937174832839540">"You haven’t set up Face Unlock."</string>
- <string name="face_error_hw_not_present" msgid="8302690289757559738">"Face Unlock is not supported on this device."</string>
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"You haven’t set up face unlock."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"Face unlock is not supported on this device."</string>
<string name="face_name_template" msgid="7004562145809595384">"Face <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -821,7 +821,7 @@
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Try again"</string>
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Try again"</string>
<string name="lockscreen_storage_locked" msgid="9167551160010625200">"Unlock for all features and data"</string>
- <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Maximum Face Unlock attempts exceeded"</string>
+ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Maximum face unlock attempts exceeded"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"No SIM card"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"No SIM card in tablet."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="3360993527792167595">"No SIM card in your Android TV device."</string>
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Uncategorised"</string>
<string name="importance_from_user" msgid="7318955817386549931">"You set the importance of these notifications."</string>
<string name="importance_from_person" msgid="9160133597262938296">"This is important because of the people involved."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Allow <xliff:g id="APP">%1$s</xliff:g> to create a new User with <xliff:g id="ACCOUNT">%2$s</xliff:g> ?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Allow <xliff:g id="APP">%1$s</xliff:g> to create a new User with <xliff:g id="ACCOUNT">%2$s</xliff:g> (a User with this account already exists) ?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Add a language"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Region preference"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Type language name"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 7961a865cd53..62f294e66438 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‎‎‏‎‎‏‎‏‎‎‏‏‎‎‏‎‏‏‎‎‏‎‏‏‏‏‏‎‎‏‏‎‎‎‏‎‎‎‎‏‎‏‏‏‏‏‎‏‏‏‎‎‏‏‏‏‎‎Uncategorized‎‏‎‎‏‎"</string>
<string name="importance_from_user" msgid="7318955817386549931">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‏‎‎‏‎‎‏‎‎‎‏‎‎‏‏‏‏‎‎‎‏‎‎‏‏‏‎‏‎‏‏‎‎‎‎‏‏‎‏‎‏‎‏‎‎‎‏‎‏‎‏‎‏‎‏‏‎You set the importance of these notifications.‎‏‎‎‏‎"</string>
<string name="importance_from_person" msgid="9160133597262938296">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‏‏‎‏‎‏‎‏‎‎‏‏‏‏‏‎‎‏‎‏‏‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‎‎‎‎‏‎‏‏‏‎‎‎‎This is important because of the people involved.‎‏‎‎‏‎"</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‎‏‏‏‏‎‏‎‏‏‎‎‎‎‏‏‎‎‎‎‎‏‎‏‎‎‏‏‎‏‎‏‎‎‎‏‏‏‎‎‎‏‏‎‏‏‏‎‎‎‎‏‏‏‎‎‏‎Allow ‎‏‎‎‏‏‎<xliff:g id="APP">%1$s</xliff:g>‎‏‎‎‏‏‏‎ to create a new User with ‎‏‎‎‏‏‎<xliff:g id="ACCOUNT">%2$s</xliff:g>‎‏‎‎‏‏‏‎ ?‎‏‎‎‏‎"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‎‎‏‏‎‏‎‏‏‎‎‏‏‎‎‏‏‏‎‎‎‎‎‎‎‎‏‎‎‎‏‏‎‏‎‎‏‎‏‏‎‎‏‏‎‏‏‎‏‏‏‏‏‏‎‏‎Allow ‎‏‎‎‏‏‎<xliff:g id="APP">%1$s</xliff:g>‎‏‎‎‏‏‏‎ to create a new User with ‎‏‎‎‏‏‎<xliff:g id="ACCOUNT">%2$s</xliff:g>‎‏‎‎‏‏‏‎ (a User with this account already exists) ?‎‏‎‎‏‎"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‏‎‎‏‏‎‎‏‏‏‎‏‎‏‏‎‏‏‏‎‎‏‎‎‎‎‎‎‏‏‎‏‎‎‎‏‎‎‏‎‏‎‎‎‏‏‎‏‏‏‏‎‎‎‎‎‎‎Add a language‎‏‎‎‏‎"</string>
<string name="country_selection_title" msgid="2954859441620215513">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‏‎‎‎‎‎‎‎‏‏‏‎‎‎‏‎‏‎‎‎‎‎‎‎‏‏‎‏‏‎‎‏‎‏‏‏‏‏‎‏‏‎‏‏‎‎‏‏‎‏‏‎‏‏‎‎‏‎Region preference‎‏‎‎‏‎"</string>
<string name="search_language_hint" msgid="7042102592055108574">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‏‏‏‎‏‎‏‎‎‏‎‎‏‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‏‎‏‎‎‏‏‎‏‎‏‏‎‎‎‏‏‏‏‎‏‏‏‏‎‎Type language name‎‏‎‎‏‎"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 0229cf5a0ed4..7346bd0b3ace 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Sin categoría"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Estableciste la importancia de estas notificaciones."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Es importante debido a las personas involucradas."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"¿Quieres permitir que <xliff:g id="APP">%1$s</xliff:g> cree un usuario nuevo con <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"¿Quieres permitir que <xliff:g id="APP">%1$s</xliff:g> cree un usuario nuevo con <xliff:g id="ACCOUNT">%2$s</xliff:g>? (Ya existe un usuario con esta cuenta)"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Agregar un idioma"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Preferencia de región"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Nombre del idioma"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index d9864fbf38f9..6c1c99230adb 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1357,7 +1357,7 @@
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"Se ha detectado un accesorio de audio analógico"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"El dispositivo adjunto no es compatible con este teléfono. Toca para obtener más información."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Depuración USB habilitada"</string>
- <string name="adb_active_notification_message" msgid="7463062450474107752">"Toca para desactivar la depuración USB."</string>
+ <string name="adb_active_notification_message" msgid="7463062450474107752">"Tocar para desactivar depuración USB"</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Seleccionar para inhabilitar la depuración USB"</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Modo de agente de prueba habilitado"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Restablece los ajustes de fábrica para inhabilitar el modo de agente de prueba."</string>
@@ -1672,8 +1672,8 @@
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"El acceso directo a accesibilidad ha desactivado <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Para utilizar <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, mantén pulsadas ambas teclas de volumen durante 3 segundos"</string>
<string name="accessibility_button_prompt_text" msgid="1176658502969738564">"Selecciona el servicio que se utilizará cuando toques el botón Accesibilidad:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8259145549733019401">"Elige el servicio que se utilizará con el gesto de accesibilidad (desliza dos dedos hacia arriba desde la parte inferior de la pantalla):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="1041435574275047665">"Elige el servicio que se utilizará con el gesto de accesibilidad (desliza tres dedos hacia arriba desde la parte inferior de la pantalla):"</string>
+ <string name="accessibility_gesture_prompt_text" msgid="8259145549733019401">"Elige el servicio que se utilizará con el gesto de accesibilidad (deslizar dos dedos hacia arriba desde la parte inferior de la pantalla):"</string>
+ <string name="accessibility_gesture_3finger_prompt_text" msgid="1041435574275047665">"Elige el servicio que se utilizará con el gesto de accesibilidad (deslizar tres dedos hacia arriba desde la parte inferior de la pantalla):"</string>
<string name="accessibility_button_instructional_text" msgid="7003212763213614833">"Para cambiar de un servicio a otro, mantén pulsado el botón de accesibilidad."</string>
<string name="accessibility_gesture_instructional_text" msgid="5261788874937410950">"Para cambiar de un servicio a otro, desliza dos dedos hacia arriba y mantén pulsada la pantalla."</string>
<string name="accessibility_gesture_3finger_instructional_text" msgid="4969448938984394550">"Para cambiar de un servicio a otro, desliza tres dedos hacia arriba y mantén pulsada la pantalla."</string>
@@ -1890,9 +1890,11 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Sin clasificar"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Tú determinas la importancia de estas notificaciones."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Esto es importante por los usuarios implicados."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"¿Permitir que <xliff:g id="APP">%1$s</xliff:g> cree un usuario con la cuenta <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"¿Permitir que <xliff:g id="APP">%1$s</xliff:g> cree un usuario con la cuenta <xliff:g id="ACCOUNT">%2$s</xliff:g> (ya existe un usuario con esta cuenta)?"</string>
- <string name="language_selection_title" msgid="2680677278159281088">"Añade un idioma"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
+ <string name="language_selection_title" msgid="2680677278159281088">"Añadir un idioma"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Preferencia de región"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Nombre de idioma"</string>
<string name="language_picker_section_suggested" msgid="8414489646861640885">"Sugeridos"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index afa8453259bb..28d12036a9b6 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -243,7 +243,7 @@
<string name="global_action_toggle_silent_mode" msgid="8219525344246810925">"Hääletu režiim"</string>
<string name="global_action_silent_mode_on_status" msgid="3289841937003758806">"Heli on VÄLJAS"</string>
<string name="global_action_silent_mode_off_status" msgid="1506046579177066419">"Heli on SEES"</string>
- <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Lennurežiim"</string>
+ <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Lennukirežiim"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Lennurežiim on SEES"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Lennurežiim on VÄLJAS"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Seaded"</string>
@@ -1672,7 +1672,7 @@
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Juurdepääsetavuse otsetee lülitas teenuse <xliff:g id="SERVICE_NAME">%1$s</xliff:g> välja"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Teenuse <xliff:g id="SERVICE_NAME">%1$s</xliff:g> kasutamiseks hoidke kolm sekundit all mõlemat helitugevuse klahvi"</string>
<string name="accessibility_button_prompt_text" msgid="1176658502969738564">"Valige, millist teenust kasutada, kui puudutate juurdepääsetavuse nuppu:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8259145549733019401">"Valige, millist teenust kasutada koos juurdepääsetavuse liigutusega (pühkige kahe sõrmega ekraanikuval alt üles):"</string>
+ <string name="accessibility_gesture_prompt_text" msgid="8259145549733019401">"Valige, millist teenust kasutada koos juurdepääsetavuse liigutusega (kahe sõrmega ekraanikuval alt üles pühkimine):"</string>
<string name="accessibility_gesture_3finger_prompt_text" msgid="1041435574275047665">"Valige, millist teenust kasutada koos juurdepääsetavuse liigutusega (kolme sõrmega ekraanikuval alt üles pühkimine):"</string>
<string name="accessibility_button_instructional_text" msgid="7003212763213614833">"Teenuste vahel vahetamiseks vajutage pikalt juurdepääsetavuse nuppu."</string>
<string name="accessibility_gesture_instructional_text" msgid="5261788874937410950">"Teenuste vahel vahetamiseks pühkige kahe sõrmega üles ja hoidke."</string>
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Kategoriseerimata"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Teie määrasite nende märguannete tähtsuse."</string>
<string name="importance_from_person" msgid="9160133597262938296">"See on tähtis osalevate inimeste tõttu."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Kas lubada rakendusel <xliff:g id="APP">%1$s</xliff:g> luua uus kasutaja kontoga <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Kas lubada rakendusel <xliff:g id="APP">%1$s</xliff:g> luua uus kasutaja kontoga <xliff:g id="ACCOUNT">%2$s</xliff:g> (selle kontoga kasutaja on juba olemas)?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Keele lisamine"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Piirkonnaeelistus"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Sisestage keele nimi"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 9932fdf7dba6..9460ee702856 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -1638,7 +1638,7 @@
<string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"PIN kode okerra."</string>
<string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Idatzi 4 eta 8 zenbaki arteko PINa."</string>
<string name="kg_invalid_sim_puk_hint" msgid="6025069204539532000">"PUK kodeak 8 zenbaki izan behar ditu."</string>
- <string name="kg_invalid_puk" msgid="3638289409676051243">"Idatzi berriro PUK kode zuzena. Hainbat saiakera oker eginez gero, betirako desgaituko da SIMa."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"Idatzi berriro PUK kode zuzena. Hainbat saiakera oker eginez gero, betiko desgaituko da SIMa."</string>
<string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"PIN kodeak ez datoz bat"</string>
<string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Eredua marrazteko saiakera gehiegi egin dira"</string>
<string name="kg_login_instructions" msgid="1100551261265506448">"Desblokeatzeko, hasi saioa Google kontuarekin."</string>
@@ -1891,8 +1891,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Kategoriarik gabea"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Zuk ezarri duzu jakinarazpen hauen garrantzia."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Garrantzitsua da eragiten dien pertsonengatik."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"<xliff:g id="APP">%1$s</xliff:g> aplikazioari <xliff:g id="ACCOUNT">%2$s</xliff:g> kontua duen erabiltzailea sortzeko baimena eman nahi diozu?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"<xliff:g id="APP">%1$s</xliff:g> aplikazioari <xliff:g id="ACCOUNT">%2$s</xliff:g> kontua duen erabiltzailea sortzeko baimena eman nahi diozu? (Badago kontu hori duen erabiltzaile bat)"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Gehitu hizkuntza"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Lurralde-hobespena"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Adierazi hizkuntza"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index cf7f37cc6740..ec5dbcb20151 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -292,7 +292,7 @@
<string name="permgrouprequest_calendar" msgid="289900767793189421">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه داده شود به تقویم شما دسترسی پیدا کند؟"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"پیامک"</string>
<string name="permgroupdesc_sms" msgid="4656988620100940350">"ارسال و مشاهده پیامک‌ها"</string>
- <string name="permgrouprequest_sms" msgid="7168124215838204719">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;b&gt; اجازه داده شود پیامک‌ها را ارسال و مشاهده کند؟"</string>
+ <string name="permgrouprequest_sms" msgid="7168124215838204719">"‏به «&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;b&gt;» اجازه داده شود پیامک ارسال و مشاهده کند؟"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"حافظه"</string>
<string name="permgroupdesc_storage" msgid="637758554581589203">"دسترسی به عکس‌ها، رسانه‌ها و فایل‌های روی دستگاهتان"</string>
<string name="permgrouprequest_storage" msgid="7885942926944299560">"‏به برنامه &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;b&gt; اجازه داده شود به عکس‌ها، رسانه، و فایل‌های موجود در دستگاهتان دسترسی داشته باشد؟"</string>
@@ -1810,8 +1810,8 @@
<string name="package_updated_device_owner" msgid="1847154566357862089">"توسط سرپرست سیستم به‌روزرسانی شد"</string>
<string name="package_deleted_device_owner" msgid="2307122077550236438">"توسط سرپرست سیستم حذف شد"</string>
<string name="confirm_battery_saver" msgid="639106420541753635">"تأیید"</string>
- <string name="battery_saver_description_with_learn_more" msgid="2108984221113106294">"بهینه‌سازی باتری فعالیت پس‌زمینه، برخی جلوه‌های دیداری، و سایر ویژگی‌های با مصرف بالای نیرو را خاموش یا محدود می‌کند تا عمر باتری افزایش یابد. "<annotation id="url">"بیشتر بدانید"</annotation></string>
- <string name="battery_saver_description" msgid="6413346684861241431">"بهینه‌سازی باتری فعالیت پس‌زمینه، برخی جلوه‌های دیداری، و سایر ویژگی‌های با مصرف بالای نیرو را خاموش یا محدود می‌کند تا عمر باتری افزایش یابد."</string>
+ <string name="battery_saver_description_with_learn_more" msgid="2108984221113106294">"«بهینه‌سازی باتری» فعالیت پس‌زمینه، برخی جلوه‌های دیداری، و سایر ویژگی‌های پرمصرف نیرو را خاموش یا محدود می‌کند تا عمر باتری افزایش یابد. "<annotation id="url">"بیشتر بدانید"</annotation></string>
+ <string name="battery_saver_description" msgid="6413346684861241431">"«بهینه‌سازی باتری» فعالیت پس‌زمینه، برخی جلوه‌های دیداری، و سایر ویژگی‌های پرمصرف نیرو را خاموش یا محدود می‌کند تا عمر باتری افزایش یابد."</string>
<string name="data_saver_description" msgid="6015391409098303235">"برای کمک به کاهش مصرف داده، «صرفه‌جویی داده» از ارسال و دریافت داده در پس‌زمینه ازطرف بعضی برنامه‌ها جلوگیری می‌کند. برنامه‌ای که درحال‌حاضر استفاده می‌کنید می‌تواند به داده‌ها دسترسی داشته باشد اما دفعات دسترسی آن محدود است.این یعنی، برای مثال، تصاویر تا زمانی که روی آن‌ها ضربه نزنید نشان داده نمی‌شوند."</string>
<string name="data_saver_enable_title" msgid="4674073932722787417">"صرفه‌جویی داده روشن شود؟"</string>
<string name="data_saver_enable_button" msgid="7147735965247211818">"روشن کردن"</string>
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"دسته‌بندی‌نشده"</string>
<string name="importance_from_user" msgid="7318955817386549931">"شما اهمیت این اعلان‌ها را تنظیم می‌کنید."</string>
<string name="importance_from_person" msgid="9160133597262938296">"به دلیل افراد درگیر مهم است."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"به <xliff:g id="APP">%1$s</xliff:g> امکان داده شود کاربر جدیدی با <xliff:g id="ACCOUNT">%2$s</xliff:g> اضافه کند؟"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"به <xliff:g id="APP">%1$s</xliff:g> امکان داده شود کاربر جدیدی با <xliff:g id="ACCOUNT">%2$s</xliff:g> ایجاد کند (کاربری با این حساب از قبل وجود دارد)؟"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"افزودن زبان"</string>
<string name="country_selection_title" msgid="2954859441620215513">"اولویت‌های منطقه"</string>
<string name="search_language_hint" msgid="7042102592055108574">"نام زبان را تایپ کنید"</string>
@@ -2006,15 +2008,15 @@
<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>
+ <string name="dynamic_mode_notification_summary" msgid="2541166298550402690">"جهت افزایش عمر باتری، «بهینه‌سازی باتری» فعال شد"</string>
<string name="battery_saver_notification_channel_name" msgid="2083316159716201806">"بهینه‌سازی باتری"</string>
- <string name="battery_saver_sticky_disabled_notification_title" msgid="6376147579378764641">"تا وقتی شارژ باتری دوباره به سطح پایین نرسد، بهینه‌سازی باتری مجدداً فعال نخواهد شد"</string>
- <string name="battery_saver_sticky_disabled_notification_summary" msgid="8090192609249817945">"باتری تا سطحی کافی شارژ شده است. تا وقتی شارژ باتری دوباره به سطح پایین نرسد، بهینه‌سازی باتری مجدداً فعال نخواهد شد."</string>
+ <string name="battery_saver_sticky_disabled_notification_title" msgid="6376147579378764641">"تا وقتی شارژ باتری دوباره به سطح پایین نرسد، «بهینه‌سازی باتری» مجدداً فعال نخواهد شد"</string>
+ <string name="battery_saver_sticky_disabled_notification_summary" msgid="8090192609249817945">"باتری درحد کافی شارژ شده است. تا وقتی شارژ باتری دوباره به سطح پایین نرسد، «بهینه‌سازی باتری» مجدداً فعال نخواهد شد."</string>
<string name="battery_saver_charged_notification_title" product="default" msgid="2960978289873161288">"تلفن <xliff:g id="CHARGE_LEVEL">%1$s</xliff:g> شارژ شد"</string>
<string name="battery_saver_charged_notification_title" product="tablet" msgid="7555713825806482451">"رایانه لوحی <xliff:g id="CHARGE_LEVEL">%1$s</xliff:g> شارژ شد"</string>
<string name="battery_saver_charged_notification_title" product="device" msgid="5954873381559605660">"دستگاه <xliff:g id="CHARGE_LEVEL">%1$s</xliff:g> شارژ شد"</string>
- <string name="battery_saver_off_notification_summary" msgid="1374222493681267143">"بهینه‌سازی باتری خاموش است. ویژگی‌ها دیگر محدود نمی‌شوند."</string>
- <string name="battery_saver_off_alternative_notification_summary" msgid="4340727818546508436">"بهینه‌سازی باتری خاموش شد. ویژگی‌ها دیگر محدود نمی‌شوند."</string>
+ <string name="battery_saver_off_notification_summary" msgid="1374222493681267143">"«بهینه‌سازی باتری» خاموش است. ویژگی‌ها دیگر محدود نمی‌شوند."</string>
+ <string name="battery_saver_off_alternative_notification_summary" msgid="4340727818546508436">"«بهینه‌سازی باتری» خاموش شد. ویژگی‌ها دیگر محدود نمی‌شوند."</string>
<string name="mime_type_folder" msgid="7111951698626315204">"پوشه"</string>
<string name="mime_type_apk" msgid="5518003630972506900">"‏برنامه Android"</string>
<string name="mime_type_generic" msgid="6833871596845900027">"فایل"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index d39bdc7614e7..a94253b9e68f 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -283,7 +283,7 @@
<string name="permgrouprequest_contacts" msgid="6032805601881764300">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; yhteystietojesi käyttöoikeuden?"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Sijainti"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"käyttää laitteen sijaintia"</string>
- <string name="permgrouprequest_location" msgid="3788275734953323491">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tämän laitteen sijainnin käyttöoikeuden?"</string>
+ <string name="permgrouprequest_location" msgid="3788275734953323491">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; oikeuden nähdä tämän laitteen sijainnin?"</string>
<string name="permgrouprequestdetail_location" msgid="1347189607421252902">"Sovellus saa sijainnin käyttöoikeuden vain silloin, kun käytät sovellusta"</string>
<string name="permgroupbackgroundrequest_location" msgid="5039063878675613235">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; käyttää laitteen sijaintia &lt;b&gt;aina&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="4597006851453417387">"Sovellus saa tällä hetkellä sijainnin käyttöoikeuden vain, jos käytät sovellusta"</string>
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Luokittelematon"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Voit valita näiden ilmoitusten tärkeyden."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Tämä on tärkeää siihen liittyvien ihmisten perusteella."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Myönnetäänkö sovellukselle <xliff:g id="APP">%1$s</xliff:g> oikeus luoda käyttäjä tilille <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Myönnetäänkö sovellukselle <xliff:g id="APP">%1$s</xliff:g> oikeus luoda käyttäjä tilille <xliff:g id="ACCOUNT">%2$s</xliff:g> (tilillä on jo käyttäjä)?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Lisää kieli"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Alueasetus"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Anna kielen nimi"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 4eae9baa1c0a..94f6acbdd080 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -100,7 +100,7 @@
<string name="peerTtyModeHco" msgid="5728602160669216784">"Mode TTY HCO demandé par un pair"</string>
<string name="peerTtyModeVco" msgid="1742404978686538049">"Mode TTY VCO demandé par un pair"</string>
<string name="peerTtyModeOff" msgid="3280819717850602205">"Mode TTY DÉSACTIVÉ demandé par un pair"</string>
- <string name="serviceClassVoice" msgid="1258393812335258019">"Google Voice"</string>
+ <string name="serviceClassVoice" msgid="1258393812335258019">"Voix"</string>
<string name="serviceClassData" msgid="872456782077937893">"Données"</string>
<string name="serviceClassFAX" msgid="5566624998840486475">"Télécopie"</string>
<string name="serviceClassSMS" msgid="2015460373701527489">"SMS"</string>
@@ -1672,10 +1672,10 @@
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Le raccourci d\'accessibilité a désactivé la fonction <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Maintenez enfoncées les deux touches de volume pendant trois secondes pour utiliser <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="1176658502969738564">"Choisissez un service à utiliser lorsque vous touchez le bouton d\'accessibilité :"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8259145549733019401">"Choisissez un service à utiliser lorsque vous utilisez le geste d\'accessibilité (balayer deux doigts du bas de l\'écran vers le haut) :"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="1041435574275047665">"Choisissez un service à utiliser lorsque vous utilisez le geste d\'accessibilité (balayer trois doigts du bas de l\'écran vers le haut) :"</string>
+ <string name="accessibility_gesture_prompt_text" msgid="8259145549733019401">"Choisissez un service à utiliser lorsque vous utilisez le geste d\'accessibilité (balayer l\'écran de bas en haut avec deux doigts) :"</string>
+ <string name="accessibility_gesture_3finger_prompt_text" msgid="1041435574275047665">"Choisissez un service à utiliser lorsque vous utilisez le geste d\'accessibilité (balayer l\'écran de bas en haut avec trois doigts) :"</string>
<string name="accessibility_button_instructional_text" msgid="7003212763213614833">"Pour basculer entre les services, maintenez le doigt sur le bouton d\'accessibilité."</string>
- <string name="accessibility_gesture_instructional_text" msgid="5261788874937410950">"Pour basculer entre les services, balayez deux doigts vers le haut et maintenez-les sur l\'écran."</string>
+ <string name="accessibility_gesture_instructional_text" msgid="5261788874937410950">"Pour basculer entre les services, balayez l\'écrfan vers le haut avec deux doigts et maintenez-les-y."</string>
<string name="accessibility_gesture_3finger_instructional_text" msgid="4969448938984394550">"Pour changer de service, balayez trois doigts vers le haut et maintenez-les sur l\'écran."</string>
<string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Zoom"</string>
<string name="user_switched" msgid="3768006783166984410">"Utilisateur actuel : <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Sans catégorie"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Vous définissez l\'importance de ces notifications."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Ces notifications sont importantes en raison des participants."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Autoriser <xliff:g id="APP">%1$s</xliff:g> à créer un profil d\'utilisateur avec le compte <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Autoriser <xliff:g id="APP">%1$s</xliff:g> à créer un profil d\'utilisateur avec le compte <xliff:g id="ACCOUNT">%2$s</xliff:g>? (Un utilisateur associé à ce compte existe déjà.)"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Ajouter une langue"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Préférences régionales"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Entrez la langue"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 1fd552ec2c85..11ba7dc7911b 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -292,7 +292,7 @@
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Permettre à &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; d\'accéder à votre agenda ?"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
<string name="permgroupdesc_sms" msgid="4656988620100940350">"envoyer et consulter des SMS"</string>
- <string name="permgrouprequest_sms" msgid="7168124215838204719">"Permettre à &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; d\'envoyer et d\'afficher des SMS ?"</string>
+ <string name="permgrouprequest_sms" msgid="7168124215838204719">"Autoriser l\'application &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à envoyer et afficher des SMS ?"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"Stockage"</string>
<string name="permgroupdesc_storage" msgid="637758554581589203">"accéder aux photos, contenus multimédias et fichiers sur votre appareil"</string>
<string name="permgrouprequest_storage" msgid="7885942926944299560">"Autoriser l\'appli &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder aux photos, contenus multimédias et fichiers sur votre appareil ?"</string>
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Sans catégorie"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Vous définissez l\'importance de ces notifications."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Ces notifications sont importantes en raison des participants."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Autoriser <xliff:g id="APP">%1$s</xliff:g> à créer un profil utilisateur avec le compte <xliff:g id="ACCOUNT">%2$s</xliff:g> ?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Autoriser <xliff:g id="APP">%1$s</xliff:g> à créer un profil utilisateur avec le compte <xliff:g id="ACCOUNT">%2$s</xliff:g> (un utilisateur associé à ce compte existe déjà) ?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Ajouter une langue"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Préférences régionales"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Saisissez la langue"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 919a13ddff77..1becada75cac 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -1094,27 +1094,27 @@
<string name="inputMethod" msgid="1653630062304567879">"Método de introdución de texto"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Accións de texto"</string>
<string name="email" msgid="4560673117055050403">"Correo electrónico"</string>
- <string name="email_desc" msgid="3638665569546416795">"Envía un correo electrónico ao enderezo seleccionado"</string>
+ <string name="email_desc" msgid="3638665569546416795">"Enviar un correo electrónico ao enderezo seleccionado"</string>
<string name="dial" msgid="1253998302767701559">"Chamar"</string>
- <string name="dial_desc" msgid="6573723404985517250">"Chama ao número de teléfono seleccionado"</string>
+ <string name="dial_desc" msgid="6573723404985517250">"Chamar ao número de teléfono seleccionado"</string>
<string name="map" msgid="5441053548030107189">"Mapa"</string>
- <string name="map_desc" msgid="1836995341943772348">"Localiza o enderezo seleccionado"</string>
+ <string name="map_desc" msgid="1836995341943772348">"Localizar o enderezo seleccionado"</string>
<string name="browse" msgid="1245903488306147205">"Abrir"</string>
- <string name="browse_desc" msgid="8220976549618935044">"Abre o URL seleccionado"</string>
+ <string name="browse_desc" msgid="8220976549618935044">"Abrir o URL seleccionado"</string>
<string name="sms" msgid="4560537514610063430">"Enviar SMS"</string>
- <string name="sms_desc" msgid="7526588350969638809">"Envía unha mensaxe ao número de teléfono seleccionado"</string>
+ <string name="sms_desc" msgid="7526588350969638809">"Enviar unha mensaxe ao número de teléfono seleccionado"</string>
<string name="add_contact" msgid="7867066569670597203">"Engadir"</string>
- <string name="add_contact_desc" msgid="4830217847004590345">"Engade o elemento aos contactos"</string>
+ <string name="add_contact_desc" msgid="4830217847004590345">"Engadir o elemento aos contactos"</string>
<string name="view_calendar" msgid="979609872939597838">"Ver"</string>
- <string name="view_calendar_desc" msgid="5828320291870344584">"Consulta a hora seleccionada no calendario"</string>
+ <string name="view_calendar_desc" msgid="5828320291870344584">"Consultar a hora seleccionada no calendario"</string>
<string name="add_calendar_event" msgid="1953664627192056206">"Programar"</string>
- <string name="add_calendar_event_desc" msgid="4326891793260687388">"Programa un evento para a data seleccionada"</string>
+ <string name="add_calendar_event_desc" msgid="4326891793260687388">"Programar un evento para a data seleccionada"</string>
<string name="view_flight" msgid="7691640491425680214">"Realizar seguimento"</string>
- <string name="view_flight_desc" msgid="3876322502674253506">"Fai un seguimento do voo seleccionado"</string>
+ <string name="view_flight_desc" msgid="3876322502674253506">"Facer un seguimento do voo seleccionado"</string>
<string name="translate" msgid="9218619809342576858">"Traducir"</string>
- <string name="translate_desc" msgid="4502367770068777202">"Traduce o texto seleccionado"</string>
+ <string name="translate_desc" msgid="4502367770068777202">"Traducir o texto seleccionado"</string>
<string name="define" msgid="7394820043869954211">"Definir"</string>
- <string name="define_desc" msgid="7910883642444919726">"Define o texto seleccionado"</string>
+ <string name="define_desc" msgid="7910883642444919726">"Definir o texto seleccionado"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Estase esgotando o espazo de almacenamento"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"É posible que algunhas funcións do sistema non funcionen"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Non hai almacenamento suficiente para o sistema. Asegúrate de ter un espazo libre de 250 MB e reinicia o dispositivo."</string>
@@ -1891,8 +1891,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Sen clasificar"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Ti defines a importancia destas notificacións."</string>
<string name="importance_from_person" msgid="9160133597262938296">"É importante polas persoas involucradas."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Queres permitir que <xliff:g id="APP">%1$s</xliff:g> cree un usuario novo con <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Queres permitir que <xliff:g id="APP">%1$s</xliff:g> cree un usuario novo con <xliff:g id="ACCOUNT">%2$s</xliff:g>? (Xa existe un usuario con esta conta)"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Engadir un idioma"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Preferencia de rexión"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Nome do idioma"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index ecf717a9d8ee..09661c15afe8 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -283,7 +283,7 @@
<string name="permgrouprequest_contacts" msgid="6032805601881764300">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને તમારા સંપર્કોને ઍક્સેસ કરવાની મંજૂરી આપીએ?"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"સ્થાન"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"આ ઉપકરણના સ્થાનને ઍક્સેસ કરવાની"</string>
- <string name="permgrouprequest_location" msgid="3788275734953323491">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને આ ઉપકરણના સ્થાનને ઍક્સેસ કરવાની મંજૂરી આપીએ?"</string>
+ <string name="permgrouprequest_location" msgid="3788275734953323491">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને આ ડિવાઇસના સ્થાનને ઍક્સેસ કરવાની મંજૂરી આપીએ?"</string>
<string name="permgrouprequestdetail_location" msgid="1347189607421252902">"જ્યારે તમે ઍપનો ઉપયોગ કરી રહ્યા હશો માત્ર ત્યારે જ ઍપ સ્થાનને ઍક્સેસ કરી શકશે"</string>
<string name="permgroupbackgroundrequest_location" msgid="5039063878675613235">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને આ ડિવાઇસનું સ્થાન &lt;b&gt;હંમેશાં&lt;/b&gt; ઍક્સેસ કરવાની મંજૂરી આપીએ?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="4597006851453417387">"હાલમાં માત્ર જ્યારે તમે ઍપનો ઉપયોગ કરી રહ્યા હશો હોય ત્યારે જ ઍપ સ્થાનને ઍક્સેસ કરી શકશે"</string>
@@ -1891,8 +1891,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"અવર્ગીકૃત"</string>
<string name="importance_from_user" msgid="7318955817386549931">"તમે આ સૂચનાઓનું મહત્વ સેટ કર્યું છે."</string>
<string name="importance_from_person" msgid="9160133597262938296">"શામેલ થયેલ લોકોને કારણે આ મહત્વપૂર્ણ છે."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"<xliff:g id="APP">%1$s</xliff:g> ને <xliff:g id="ACCOUNT">%2$s</xliff:g> સાથે એક નવા વપરાશકર્તાને બનાવવાની મંજૂરી આપીએ?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"<xliff:g id="ACCOUNT">%2$s</xliff:g> સાથે <xliff:g id="APP">%1$s</xliff:g> ને એક નવા વપરાશકર્તાને બનાવવાની મંજૂરી આપીએ (આ એકાઉન્ટ સાથેના એક વપરાશકર્તા પહેલાંથી અસ્તિત્વમાં છે)?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"ભાષા ઉમેરો"</string>
<string name="country_selection_title" msgid="2954859441620215513">"પ્રદેશ પસંદગી"</string>
<string name="search_language_hint" msgid="7042102592055108574">"ભાષાનું નામ ટાઇપ કરો"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 53e9217c500a..f21d138f14f0 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1155,7 +1155,7 @@
<string name="whichImageCaptureApplicationNamed" msgid="8619384150737825003">"%1$s के साथ चित्र कैप्चर करें"</string>
<string name="whichImageCaptureApplicationLabel" msgid="6390303445371527066">"चित्र कैप्चर करें"</string>
<string name="alwaysUse" msgid="4583018368000610438">"इस कार्रवाई के लिए डिफ़ॉल्‍ट के तौर पर इस्तेमाल करें"</string>
- <string name="use_a_different_app" msgid="8134926230585710243">"किसी भिन्न ऐप्स का उपयोग करें"</string>
+ <string name="use_a_different_app" msgid="8134926230585710243">"किसी दूसरे ऐप्लिकेशन का इस्तेमाल करें"</string>
<string name="clearDefaultHintMsg" msgid="3252584689512077257">"सिस्‍टम सेटिंग और डाउनलोड किए गए ऐप में डिफ़ॉल्‍ट साफ़ करें."</string>
<string name="chooseActivity" msgid="7486876147751803333">"कोई कार्रवाई चुनें"</string>
<string name="chooseUsbActivity" msgid="6894748416073583509">"USB डिवाइस के लिए कोई ऐप्स चुनें"</string>
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"बिना किसी श्रेणी के"</string>
<string name="importance_from_user" msgid="7318955817386549931">"आपने इन सूचनाओं की अहमियत सेट की है."</string>
<string name="importance_from_person" msgid="9160133597262938296">"यह मौजूद व्यक्तियों के कारण महत्वपूर्ण है."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"<xliff:g id="APP">%1$s</xliff:g> को <xliff:g id="ACCOUNT">%2$s</xliff:g> के ज़रिये एक नया उपयोगकर्ता बनाने दें?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"<xliff:g id="APP">%1$s</xliff:g> को <xliff:g id="ACCOUNT">%2$s</xliff:g> के ज़रिये एक नया उपयोगकर्ता बनाने दें (इस खाते वाले एक उपयोगकर्ता पहले से मौजूद हैं)?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"भाषा जोड़ें"</string>
<string name="country_selection_title" msgid="2954859441620215513">"क्षेत्र प्राथमिकता"</string>
<string name="search_language_hint" msgid="7042102592055108574">"भाषा का नाम लिखें"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 4908780d38da..25007d514064 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1696,7 +1696,7 @@
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Prečac pristupačnosti isključio je uslugu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Pritisnite i zadržite tipke za glasnoću na tri sekunde da biste koristili uslugu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="1176658502969738564">"Odaberite uslugu koju ćete upotrebljavati kad dodirnete gumb pristupačnosti:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8259145549733019401">"Odaberite uslugu koju ćete upotrebljavati uz pokret pristupačnosti (prelazak s dva prsta prema gore od dna zaslona):"</string>
+ <string name="accessibility_gesture_prompt_text" msgid="8259145549733019401">"Odaberite za što će se upotrebljavati pokret pristupačnosti (prelazak s dva prsta prema gore od dna zaslona):"</string>
<string name="accessibility_gesture_3finger_prompt_text" msgid="1041435574275047665">"Odaberite uslugu koju ćete upotrebljavati uz pokret pristupačnosti (prelazak s tri prsta prema gore od dna zaslona):"</string>
<string name="accessibility_button_instructional_text" msgid="7003212763213614833">"Da biste prešli na neku drugu uslugu, dodirnite i zadržite gumb pristupačnosti."</string>
<string name="accessibility_gesture_instructional_text" msgid="5261788874937410950">"Da biste prešli na neku drugu uslugu, prijeđite s dva prsta prema gore i zadržite."</string>
@@ -1924,8 +1924,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Nema kategorije"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Postavili ste važnost tih obavijesti."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Važno je zbog uključenih osoba."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Želite li dopustiti aplikaciji <xliff:g id="APP">%1$s</xliff:g> da izradi novog korisnika s računom <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Želite li dopustiti aplikaciji <xliff:g id="APP">%1$s</xliff:g> da izradi novog korisnika s računom <xliff:g id="ACCOUNT">%2$s</xliff:g> (korisnik s tim računom već postoji)?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Dodavanje jezika"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Postavke regije"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Unesite naziv jezika"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index e32457a4f7e8..0487601ac29b 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Nincs kategóriába sorolva"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Ön állította be ezen értesítések fontossági szintjét."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Ez az üzenet a résztvevők miatt fontos."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Engedélyezi a(z) <xliff:g id="APP">%1$s</xliff:g> számára, hogy új felhasználót hozzon létre a(z) <xliff:g id="ACCOUNT">%2$s</xliff:g> fiókkal?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Engedélyezi a(z) <xliff:g id="APP">%1$s</xliff:g> számára, hogy új felhasználót hozzon létre a(z) <xliff:g id="ACCOUNT">%2$s</xliff:g> fiókkal? (Már létezik felhasználó ezzel a fiókkal.)"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Nyelv hozzáadása"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Régió beállítása"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Adja meg a nyelvet"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 5af2235ab536..e6223f0ce438 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -1356,9 +1356,9 @@
<string name="usb_power_notification_message" msgid="4647527153291917218">"Միացված սարքի լիցքավորում: Հպեք՝ ավելի շատ ընտրանքների համար:"</string>
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"Հայտնաբերված է անալոգային աուդիո լրասարք"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"Կցված սարքը համատեղելի չէ այս հեռախոսի հետ: Հպեք` ավելին իմանալու համար:"</string>
- <string name="adb_active_notification_title" msgid="6729044778949189918">"USB վրիպազերծումը միացված է"</string>
- <string name="adb_active_notification_message" msgid="7463062450474107752">"Հպեք՝ USB վրիպազերծումն անջատելու համար"</string>
- <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Ընտրել` USB կարգաբերումը կասեցնելու համար:"</string>
+ <string name="adb_active_notification_title" msgid="6729044778949189918">"USB-ով վրիպազերծումը միացված է"</string>
+ <string name="adb_active_notification_message" msgid="7463062450474107752">"Հպեք՝ USB-ով վրիպազերծումն անջատելու համար"</string>
+ <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Սեղմեք՝ USB-ով վրիպազերծումն անջատելու համար:"</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Թեստային ռեժիմը միացված է"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Թեստային ռեժիմն անջատելու համար զրոյացրեք կարգավորումները։"</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB միացքում ջուր կամ աղտ է հայտնաբերվել"</string>
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Չդասակարգված"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Դուք սահմանել եք այս ծանուցումների կարևորությունը:"</string>
<string name="importance_from_person" msgid="9160133597262938296">"Կարևոր է, քանի որ որոշակի մարդիկ են ներգրավված:"</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Թույլատրե՞լ <xliff:g id="APP">%1$s</xliff:g> հավելվածին <xliff:g id="ACCOUNT">%2$s</xliff:g> հաշվով նոր Օգտատեր ստեղծել:"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Թույլատրե՞լ <xliff:g id="APP">%1$s</xliff:g> հավելվածին <xliff:g id="ACCOUNT">%2$s</xliff:g> հաշվով նոր Օգտատեր ստեղծել (նման հաշվով Օգտատեր արդեն գոյություն ունի):"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Ավելացնել լեզու"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Նախընտրելի տարածաշրջան"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Մուտքագրեք լեզուն"</string>
@@ -1930,7 +1932,7 @@
<string name="app_category_maps" msgid="5878491404538024367">"Քարտեզներ և նավարկում"</string>
<string name="app_category_productivity" msgid="3742083261781538852">"Արդյունավետություն"</string>
<string name="device_storage_monitor_notification_channel" msgid="3295871267414816228">"Սարքի հիշողություն"</string>
- <string name="adb_debugging_notification_channel_tv" msgid="5537766997350092316">"USB վրիպազերծում"</string>
+ <string name="adb_debugging_notification_channel_tv" msgid="5537766997350092316">"USB-ով վրիպազերծում"</string>
<string name="time_picker_hour_label" msgid="2979075098868106450">"ժամ"</string>
<string name="time_picker_minute_label" msgid="5168864173796598399">"րոպե"</string>
<string name="time_picker_header_text" msgid="143536825321922567">"Ժամը"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 0ebabaf1ce04..3c6ee839dbab 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1258,7 +1258,7 @@
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Hubungkan"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Semua jaringan"</string>
<string name="wifi_suggestion_title" msgid="6396033039578436801">"Izinkan jaringan Wi-Fi yang disarankan?"</string>
- <string name="wifi_suggestion_content" msgid="5603992011371520746">"<xliff:g id="NAME">%s</xliff:g> jaringan yang disarankan. Perangkat dapat terhubung secara otomatis."</string>
+ <string name="wifi_suggestion_content" msgid="5603992011371520746">"Jaringan yang disarankan <xliff:g id="NAME">%s</xliff:g>. Perangkat dapat terhubung secara otomatis."</string>
<string name="wifi_suggestion_action_allow_app" msgid="7978995387498669901">"Izinkan"</string>
<string name="wifi_suggestion_action_disallow_app" msgid="6434097275967940372">"Lain kali"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi akan aktif otomatis"</string>
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Belum dikategorikan"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Anda menyetel nilai penting notifikasi ini."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Ini penting karena orang-orang yang terlibat."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Izinkan <xliff:g id="APP">%1$s</xliff:g> membuat Pengguna baru dengan <xliff:g id="ACCOUNT">%2$s</xliff:g> ?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Izinkan <xliff:g id="APP">%1$s</xliff:g> membuat Pengguna baru dengan <xliff:g id="ACCOUNT">%2$s</xliff:g> (Pengguna dengan akun ini sudah ada) ?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Tambahkan bahasa"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Preferensi wilayah"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Ketik nama bahasa"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 296e44142755..59cd4c165a50 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -1891,8 +1891,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Óflokkað"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Þú stilltir mikilvægi þessara tilkynninga."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Þetta er mikilvægt vegna fólksins sem tekur þátt í þessu."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Leyfa <xliff:g id="APP">%1$s</xliff:g> að stofna nýjan notanda með <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Leyfa <xliff:g id="APP">%1$s</xliff:g> að stofna nýjan notanda með <xliff:g id="ACCOUNT">%2$s</xliff:g> (notandi með þennan reikning er þegar fyrir hendi)?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Bæta við tungumáli"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Svæðisval"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Sláðu inn heiti tungumáls"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 501ef1a78959..cbc99896202c 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Senza categoria"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Stabilisci tu l\'importanza di queste notifiche."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Importante a causa delle persone coinvolte."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Consentire a <xliff:g id="APP">%1$s</xliff:g> di creare un nuovo utente con <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Consentire a <xliff:g id="APP">%1$s</xliff:g> di creare un nuovo utente con <xliff:g id="ACCOUNT">%2$s</xliff:g>? (Esiste già un utente con questo account)"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Aggiungi una lingua"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Area geografica preferita"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Digita nome lingua"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index a2031f8b26fd..0a721a82ee2e 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -102,7 +102,7 @@
<string name="peerTtyModeHco" msgid="5728602160669216784">"‏העמית ביקש TTY במצב HCO"</string>
<string name="peerTtyModeVco" msgid="1742404978686538049">"‏העמית ביקש TTY במצב VCO"</string>
<string name="peerTtyModeOff" msgid="3280819717850602205">"‏העמית ביקש TTY במצב OFF"</string>
- <string name="serviceClassVoice" msgid="1258393812335258019">"Google Voice"</string>
+ <string name="serviceClassVoice" msgid="1258393812335258019">"קול"</string>
<string name="serviceClassData" msgid="872456782077937893">"Google Data"</string>
<string name="serviceClassFAX" msgid="5566624998840486475">"פקס"</string>
<string name="serviceClassSMS" msgid="2015460373701527489">"SMS"</string>
@@ -1958,8 +1958,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"ללא שיוך לקטגוריה"</string>
<string name="importance_from_user" msgid="7318955817386549931">"עליך להגדיר את החשיבות של ההתראות האלה."</string>
<string name="importance_from_person" msgid="9160133597262938296">"ההודעה חשובה בשל האנשים המעורבים."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"האם לאפשר ל-<xliff:g id="APP">%1$s</xliff:g> ליצור משתמש חדש לחשבון <xliff:g id="ACCOUNT">%2$s</xliff:g> ?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"האם לאפשר ל-<xliff:g id="APP">%1$s</xliff:g> ליצור משתמש חדש לחשבון <xliff:g id="ACCOUNT">%2$s</xliff:g> (כבר קיים משתמש לחשבון הזה) ?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"הוספת שפה"</string>
<string name="country_selection_title" msgid="2954859441620215513">"העדפת אזור"</string>
<string name="search_language_hint" msgid="7042102592055108574">"הקלד שם שפה"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index a54d0ec60922..fe45c1fa9486 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1258,7 +1258,7 @@
<string name="wifi_available_action_connect" msgid="2635699628459488788">"接続"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"すべてのネットワーク"</string>
<string name="wifi_suggestion_title" msgid="6396033039578436801">"Wi‑Fi ネットワーク候補を許可しますか?"</string>
- <string name="wifi_suggestion_content" msgid="5603992011371520746">"<xliff:g id="NAME">%s</xliff:g> からのネットワーク候補にデバイスが自動的に接続される可能性があります。"</string>
+ <string name="wifi_suggestion_content" msgid="5603992011371520746">"<xliff:g id="NAME">%s</xliff:g> からのネットワーク候補に、デバイスが自動的に接続される可能性があります。"</string>
<string name="wifi_suggestion_action_allow_app" msgid="7978995387498669901">"許可"</string>
<string name="wifi_suggestion_action_disallow_app" msgid="6434097275967940372">"許可しない"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi-Fi は自動的にオンになります"</string>
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"カテゴリなし"</string>
<string name="importance_from_user" msgid="7318955817386549931">"このような通知の重要度を設定します。"</string>
<string name="importance_from_person" msgid="9160133597262938296">"関係するユーザーのため、この設定は重要です。"</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"<xliff:g id="APP">%1$s</xliff:g> が <xliff:g id="ACCOUNT">%2$s</xliff:g> で新しいユーザーを作成できるようにしますか?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"<xliff:g id="APP">%1$s</xliff:g> が <xliff:g id="ACCOUNT">%2$s</xliff:g> で新しいユーザーを作成できるようにしますか?(このアカウントのユーザーはすでに存在します)"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"言語を追加"</string>
<string name="country_selection_title" msgid="2954859441620215513">"地域設定"</string>
<string name="search_language_hint" msgid="7042102592055108574">"言語名を入力"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 0376336f1122..00afe0263bf5 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"კატეგორიის გარეშე"</string>
<string name="importance_from_user" msgid="7318955817386549931">"ამ შეტყობინებების მნიშვნელობის დონე განისაზღვრა თქვენ მიერ."</string>
<string name="importance_from_person" msgid="9160133597262938296">"მნიშვნელოვანია ჩართული მომხმარებლების გამო."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"მიეცეს უფლება <xliff:g id="APP">%1$s</xliff:g>-ს, <xliff:g id="ACCOUNT">%2$s</xliff:g>-ის მეშვეობით ახალი მომხმარებელი შექმნას ?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"მიეცეს უფლება <xliff:g id="APP">%1$s</xliff:g>-ს, <xliff:g id="ACCOUNT">%2$s</xliff:g>-ის მეშვეობით ახალი მომხმარებელი შექმნას (ამ ანგარიშის მქონე მომხმარებელი უკვე არსებობს) ?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"ენის დამატება"</string>
<string name="country_selection_title" msgid="2954859441620215513">"რეგიონის პარამეტრები"</string>
<string name="search_language_hint" msgid="7042102592055108574">"აკრიფეთ ენის სახელი"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 68d63c738a4c..81f512b6a011 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -1891,8 +1891,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Санатқа жатқызылмаған"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Сіз осы хабарландырулардың маңыздылығын орнатасыз."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Қатысты адамдарға байланысты бұл маңызды."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACCOUNT">%2$s</xliff:g> есептік жазбасы бар жаңа пайдаланушы жасауға рұқсат ету керек пе?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACCOUNT">%2$s</xliff:g> есептік жазбасында жаңа пайдаланушы жасауға рұқсат ету керек пе (осы есептік жазбасы бар пайдаланушы әлдеқашан бар) ?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Тілді қосу"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Аймақ параметрі"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Тіл атауын теріңіз"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 00dc4223dc3c..539747b9652b 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -1892,8 +1892,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"មិន​​បែងចែក​ប្រភេទ"</string>
<string name="importance_from_user" msgid="7318955817386549931">"អ្នកបានកំណត់សារៈសំខាន់នៃការជូនដំណឹងទាំងនេះ"</string>
<string name="importance_from_person" msgid="9160133597262938296">"វាមានសារៈសំខាន់ដោយសារតែមនុស្សដែលពាក់ព័ន្ធ"</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"អនុញ្ញាតឲ្យ <xliff:g id="APP">%1$s</xliff:g> បង្កើតអ្នកប្រើថ្មីដោយប្រើ <xliff:g id="ACCOUNT">%2$s</xliff:g> ឬទេ?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"អនុញ្ញាតឲ្យ <xliff:g id="APP">%1$s</xliff:g> បង្កើតអ្នកប្រើថ្មីដោយប្រើ <xliff:g id="ACCOUNT">%2$s</xliff:g> (មានអ្នកប្រើសម្រាប់គណនីនេះរួចហើយ) ឬទេ?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"បន្ថែមភាសា"</string>
<string name="country_selection_title" msgid="2954859441620215513">"ចំណូលចិត្តតំបន់"</string>
<string name="search_language_hint" msgid="7042102592055108574">"វាយបញ្ចូលឈ្មោះភាសា"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 090227182914..c7d0feae9b34 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -1155,7 +1155,7 @@
<string name="whichImageCaptureApplicationNamed" msgid="8619384150737825003">"%1$s ಜೊತೆ ಚಿತ್ರ ಕ್ಯಾಪ್ಚರ್ ಮಾಡಿ"</string>
<string name="whichImageCaptureApplicationLabel" msgid="6390303445371527066">"ಚಿತ್ರ ಕ್ಯಾಪ್ಚರ್ ಮಾಡಿ"</string>
<string name="alwaysUse" msgid="4583018368000610438">"ಈ ಕ್ರಿಯೆಗೆ ಡಿಫಾಲ್ಟ್ ಆಗಿ ಬಳಸಿ."</string>
- <string name="use_a_different_app" msgid="8134926230585710243">"ಬೇರೆಯ ಅಪ್ಲಿಕೇಶನ್ ಬಳಸಿ"</string>
+ <string name="use_a_different_app" msgid="8134926230585710243">"ಬೇರೊಂದು ಆ್ಯಪ್ ಬಳಸಿ"</string>
<string name="clearDefaultHintMsg" msgid="3252584689512077257">"ಸಿಸ್ಟಂ ಸೆಟ್ಟಿಂಗ್‌ಗಳು &gt; ಅಪ್ಲಿಕೇಶನ್‌ಗಳು &gt; ಡೌನ್‌ಲೋಡ್ ಮಾಡಲಾದ ಡಿಫಾಲ್ಟ್‌‌ ಅನ್ನು ತೆರವುಗೊಳಿಸಿ."</string>
<string name="chooseActivity" msgid="7486876147751803333">"ಕ್ರಿಯೆಯನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
<string name="chooseUsbActivity" msgid="6894748416073583509">"USB ಸಾಧನಕ್ಕೆ ಅಪ್ಲಿಕೇಶನ್‌‌ವೊಂದನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
@@ -1891,8 +1891,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"ವರ್ಗೀಕರಿಸದಿರುವುದು"</string>
<string name="importance_from_user" msgid="7318955817386549931">"ನೀವು ಈ ಅಧಿಸೂಚನೆಗಳ ಪ್ರಾಮುಖ್ಯತೆಯನ್ನು ಹೊಂದಿಸಿರುವಿರಿ."</string>
<string name="importance_from_person" msgid="9160133597262938296">"ಜನರು ತೊಡಗಿಕೊಂಡಿರುವ ಕಾರಣ ಇದು ಅತ್ಯಂತ ಪ್ರಮುಖವಾಗಿದೆ."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"<xliff:g id="ACCOUNT">%2$s</xliff:g> ಮೂಲಕ ಹೊಸ ಬಳಕೆದಾರರನ್ನು ರಚಿಸಲು <xliff:g id="APP">%1$s</xliff:g> ಗೆ ಅನುಮತಿಸುವುದೇ ?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"<xliff:g id="ACCOUNT">%2$s</xliff:g> (ಈ ಖಾತೆಯ ಬಳಕೆದಾರರು ಈಗಾಗಲೇ ಅಸ್ತಿತ್ವದಲ್ಲಿದ್ದಾರೆ) ಮೂಲಕ ಹೊಸ ಬಳಕೆದಾರರನ್ನು ರಚಿಸಲು <xliff:g id="APP">%1$s</xliff:g> ಗೆ ಅನುಮತಿಸುವುದೇ ?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"ಭಾಷೆ ಸೇರಿಸಿ"</string>
<string name="country_selection_title" msgid="2954859441620215513">"ಪ್ರದೇಶ ಪ್ರಾಶಸ್ತ್ಯ"</string>
<string name="search_language_hint" msgid="7042102592055108574">"ಭಾಷೆ ಹೆಸರನ್ನು ಟೈಪ್ ಮಾಡಿ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 669bb962da6d..c0dd8b127514 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1345,7 +1345,7 @@
<string name="no_permissions" msgid="7283357728219338112">"권한 필요 없음"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"비용이 부과될 수 있습니다."</string>
<string name="dlg_ok" msgid="7376953167039865701">"확인"</string>
- <string name="usb_charging_notification_title" msgid="1595122345358177163">"이 기기를 USB로 충전"</string>
+ <string name="usb_charging_notification_title" msgid="1595122345358177163">"이 기기를 USB로 충전 중"</string>
<string name="usb_supplying_notification_title" msgid="4631045789893086181">"USB를 통해 연결된 기기 충전"</string>
<string name="usb_mtp_notification_title" msgid="4238227258391151029">"USB 파일 전송 사용 설정됨"</string>
<string name="usb_ptp_notification_title" msgid="5425857879922006878">"USB를 통해 PTP 사용 설정됨"</string>
@@ -1857,8 +1857,8 @@
<string name="zen_mode_downtime_feature_name" msgid="2626974636779860146">"다운타임"</string>
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"평일 밤"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"주말"</string>
- <string name="zen_mode_default_events_name" msgid="8158334939013085363">"캘린더 일정 중"</string>
- <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"수면 중"</string>
+ <string name="zen_mode_default_events_name" msgid="8158334939013085363">"캘린더 일정"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"수면 시간"</string>
<string name="muted_by" msgid="5942954724562097128">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g>(이)가 일부 소리를 음소거함"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"사용 중인 기기 내부에 문제가 발생했습니다. 초기화할 때까지 불안정할 수 있습니다."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"사용 중인 기기 내부에 문제가 발생했습니다. 자세한 내용은 제조업체에 문의하세요."</string>
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"지정된 카테고리 없음"</string>
<string name="importance_from_user" msgid="7318955817386549931">"이러한 알림의 중요도를 설정했습니다."</string>
<string name="importance_from_person" msgid="9160133597262938296">"관련된 사용자가 있으므로 중요합니다."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"<xliff:g id="APP">%1$s</xliff:g>이(가) <xliff:g id="ACCOUNT">%2$s</xliff:g>(으)로 신규 사용자를 만들도록 허용하시겠습니까?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"<xliff:g id="APP">%1$s</xliff:g>이(가) <xliff:g id="ACCOUNT">%2$s</xliff:g>(이 계정의 사용자가 이미 있음)(으)로 신규 사용자를 만들도록 허용하시겠습니까?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"언어 추가"</string>
<string name="country_selection_title" msgid="2954859441620215513">"지역 환경설정"</string>
<string name="search_language_hint" msgid="7042102592055108574">"언어 이름 입력"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index e448a307589e..91825630f34e 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -1257,7 +1257,7 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Бардык тармактарды көрүү үчүн басыңыз"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Туташуу"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Бардык тармактар"</string>
- <string name="wifi_suggestion_title" msgid="6396033039578436801">"Сунушталган Wi‑Fi тармактарына туташтырылсынбы?"</string>
+ <string name="wifi_suggestion_title" msgid="6396033039578436801">"Сунушталган Wi‑Fi тармактарына туташасызбы?"</string>
<string name="wifi_suggestion_content" msgid="5603992011371520746">"<xliff:g id="NAME">%s</xliff:g> сунуштаган тармактар. Түзмөк автоматтык түрдө туташышы мүмкүн."</string>
<string name="wifi_suggestion_action_allow_app" msgid="7978995387498669901">"Уруксат берүү"</string>
<string name="wifi_suggestion_action_disallow_app" msgid="6434097275967940372">"Жок, рахмат"</string>
@@ -1346,7 +1346,7 @@
<string name="no_permissions" msgid="7283357728219338112">"Эч уруксаттын кереги жок"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"бул үчүн акы алынышы мүмкүн"</string>
<string name="dlg_ok" msgid="7376953167039865701">"Жарайт"</string>
- <string name="usb_charging_notification_title" msgid="1595122345358177163">"Бул түзмөк USB аркылуу кубатталууда"</string>
+ <string name="usb_charging_notification_title" msgid="1595122345358177163">"USB аркылуу кубатталууда"</string>
<string name="usb_supplying_notification_title" msgid="4631045789893086181">"USB аркылуу туташкан түзмөк кубатталууда"</string>
<string name="usb_mtp_notification_title" msgid="4238227258391151029">"USB аркылуу файл өткөрүү режими күйгүзүлдү"</string>
<string name="usb_ptp_notification_title" msgid="5425857879922006878">"USB аркылуу PTP режими күйгүзүлдү"</string>
@@ -1358,7 +1358,7 @@
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"Аналогдук аудио жабдуу табылды"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"Тиркелген түзмөк бул телефонго шайкеш келбейт. Көбүрөөк маалымат алуу үчүн таптап коюңуз."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Мүчүлүштүктөрдү USB аркылуу оңдоо иштетилген"</string>
- <string name="adb_active_notification_message" msgid="7463062450474107752">"USB аркылуу мүчүлүштүктөрдү оңдоону өчүрүү үчүн таптаңыз"</string>
+ <string name="adb_active_notification_message" msgid="7463062450474107752">"USB арклуу мүчүлштктрдү оңдоону өчрүү үчүн басып коюңуз"</string>
<!-- no translation found for adb_active_notification_message (8470296818270110396) -->
<skip />
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Сыноо программасынын режими иштетилди"</string>
@@ -1892,8 +1892,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Категорияларга бөлүнгөн эмес"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Бул эскертмелердин маанилүүлүгүн белгиледиңиз."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Булар сиз үчүн маанилүү адамдар."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"<xliff:g id="APP">%1$s</xliff:g> колдонмосу <xliff:g id="ACCOUNT">%2$s</xliff:g> аккаунту менен жаңы колдонуучу түзө берсинби?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"<xliff:g id="APP">%1$s</xliff:g> колдонмосуна <xliff:g id="ACCOUNT">%2$s</xliff:g> аккаунту үчүн жаңы колдонуучу түзгөнгө уруксат бересизби (мындай аккаунту бар колдонуучу мурунтан эле бар)?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Тил кошуу"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Чөлкөмдүк жөндөөлөр"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Тилди киргизиңиз"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 56e0a70c3746..0189c081b2b8 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"ບໍ່​ມີ​ໝວດ​ໝູ່"</string>
<string name="importance_from_user" msgid="7318955817386549931">"ທ່ານຕັ້ງຄວາມສຳຄັນຂອງການແຈ້ງເຕືອນເຫຼົ່ານີ້."</string>
<string name="importance_from_person" msgid="9160133597262938296">"ຂໍ້ຄວາມນີ້ສຳຄັນເນື່ອງຈາກບຸກຄົນທີ່ກ່ຽວຂ້ອງ."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"ອະນຸຍາດໃຫ້ <xliff:g id="APP">%1$s</xliff:g> ສ້າງຜູ້ໃຊ້ໃໝ່ສຳລັບ <xliff:g id="ACCOUNT">%2$s</xliff:g> ບໍ?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"ອະນຸຍາດໃຫ້ <xliff:g id="APP">%1$s</xliff:g> ສ້າງຜູ້ໃຊ້ໃໝ່ສຳລັບ <xliff:g id="ACCOUNT">%2$s</xliff:g> (ຜູ້ໃຊ້ສຳລັບບັນຊີນີ້ມີຢູ່ແລ້ວ) ບໍ?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"ເພີ່ມພາສາ"</string>
<string name="country_selection_title" msgid="2954859441620215513">"ການຕັ້ງຄ່າພາກພື້ນ"</string>
<string name="search_language_hint" msgid="7042102592055108574">"ພິມຊື່ພາສາ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index e674c2a299d7..0d29c84f6560 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1958,8 +1958,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Be kategorijos"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Galite nustatyti šių pranešimų svarbą."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Tai svarbu dėl susijusių žmonių."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Leisti „<xliff:g id="APP">%1$s</xliff:g>“ kurti naują <xliff:g id="ACCOUNT">%2$s</xliff:g> naudotoją?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Leisti „<xliff:g id="APP">%1$s</xliff:g>“ kurti naują <xliff:g id="ACCOUNT">%2$s</xliff:g> naudotoją (šią paskyrą naudojantis naudotojas jau yra)?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Pridėkite kalbą"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Regiono nuostata"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Įveskite kalbos pav."</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 96cb3cd52d9c..2547ba92f9c5 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1924,8 +1924,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Nav kategorijas"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Jūs iestatījāt šo paziņojumu svarīguma līmeni."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Tas ir svarīgi iesaistīto personu dēļ."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Vai atļaut lietotnei <xliff:g id="APP">%1$s</xliff:g> izveidot jaunu lietotāju, izmantojot e-pasta adresi <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Vai atļaut lietotnei <xliff:g id="APP">%1$s</xliff:g> izveidot jaunu lietotāju, izmantojot e-pasta adresi <xliff:g id="ACCOUNT">%2$s</xliff:g> (lietotājs ar šādu kontu jau pastāv)?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Pievienot valodu"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Reģiona preference"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Ierakstiet valodas nosaukumu"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 39ad811d477d..9949eaa7ce7b 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -243,7 +243,7 @@
<string name="global_action_toggle_silent_mode" msgid="8219525344246810925">"Тивок режим"</string>
<string name="global_action_silent_mode_on_status" msgid="3289841937003758806">"Звукот е исклучен"</string>
<string name="global_action_silent_mode_off_status" msgid="1506046579177066419">"Звукот е вклучен"</string>
- <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Режим на работа во авион"</string>
+ <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Авионски режим"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Режимот на работа во авион е вклучен"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Режимот на работа во авион е исклучен"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Поставки"</string>
@@ -1358,7 +1358,7 @@
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"Откриен е аналоген аудиододаток"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"Приложениот уред не е компатибилен со телефонов. Допрете за да дознаете повеќе."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Поврзано е отстранување грешки преку USB"</string>
- <string name="adb_active_notification_message" msgid="7463062450474107752">"Допрете за да го исклучите отстранувањето грешки преку USB"</string>
+ <string name="adb_active_notification_message" msgid="7463062450474107752">"Допрете за да го исклучите"</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Изберете за да се оневозможи отстранување грешки на USB."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Овозможен е режимот на рамка за тестирање"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Извршете фабричко ресетирање за да го оневозможите режимот на рамка за тестирање."</string>
@@ -1893,8 +1893,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Некатегоризирано"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Ја поставивте важноста на известувањава."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Ова е важно заради луѓето кои се вклучени."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Дозволувате ли <xliff:g id="APP">%1$s</xliff:g> да создаде нов корисник со <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Дозволувате ли <xliff:g id="APP">%1$s</xliff:g> да создаде нов корисник со <xliff:g id="ACCOUNT">%2$s</xliff:g> (веќе постои корисник со оваа сметка)?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Додај јазик"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Претпочитувања за регион"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Внеси име на јазик"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 8588031305cb..f71115806013 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -1891,8 +1891,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"വർഗ്ഗീകരിച്ചിട്ടില്ലാത്ത"</string>
<string name="importance_from_user" msgid="7318955817386549931">"ഈ അറിയിപ്പുകളുടെ പ്രാധാന്യം നിങ്ങൾ സജ്ജീകരിച്ചു."</string>
<string name="importance_from_person" msgid="9160133597262938296">"ഉൾപ്പെട്ടിട്ടുള്ള ആളുകളെ കണക്കിലെടുക്കുമ്പോള്‍ ഇത് പ്രധാനപ്പെട്ടതാണ്‌."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"<xliff:g id="ACCOUNT">%2$s</xliff:g> എന്ന അക്കൗണ്ട് ഉപയോഗിച്ച് പുതിയൊരു ഉപയോക്താവിനെ സൃഷ്ടിക്കാൻ <xliff:g id="APP">%1$s</xliff:g> എന്ന ആപ്പിനെ അനുവദിക്കണോ?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"<xliff:g id="ACCOUNT">%2$s</xliff:g> എന്ന അക്കൗണ്ട് (ഈ അക്കൗണ്ട് ഉപയോഗിച്ചുള്ള ഒരു ഉപയോക്താവ് ഇതിനകം തന്നെ നിലവിലുണ്ട്) ഉപയോഗിച്ച് പുതിയൊരു ഉപയോക്താവിനെ സൃഷ്ടിക്കാൻ <xliff:g id="APP">%1$s</xliff:g> എന്ന ആപ്പിനെ അനുവദിക്കണോ?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"ഒരു ഭാഷ ചേർക്കുക"</string>
<string name="country_selection_title" msgid="2954859441620215513">"മേഖലാ മുൻഗണന"</string>
<string name="search_language_hint" msgid="7042102592055108574">"ഭാഷയുടെ പേര് ടൈപ്പുചെയ്യുക"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 36728025e8c6..75700287a2fa 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Ангилаагүй"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Та эдгээр мэдэгдлийн ач холбогдлыг тогтоосон."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Оролцсон хүмүүсээс шалтгаалан энэ нь өндөр ач холбогдолтой."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"<xliff:g id="APP">%1$s</xliff:g>-г <xliff:g id="ACCOUNT">%2$s</xliff:g>-р шинэ Хэрэглэгч үүсгэхийг зөвшөөрөх үү?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"<xliff:g id="APP">%1$s</xliff:g>-г <xliff:g id="ACCOUNT">%2$s</xliff:g>-р шинэ хэрэглэгч үүсгэхийг зөвшөөрөх үү (ийм бүртгэлтэй хэрэглэгч аль хэдийн байна) ?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Хэл нэмэх"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Бүс нутгийн тохиргоо"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Улсын хэлийг бичнэ үү"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index c58ae6124ffc..e22bca049ece 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -1673,7 +1673,7 @@
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"प्रवेशयोग्यता शॉर्टकटने <xliff:g id="SERVICE_NAME">%1$s</xliff:g> बंद केली"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> वापरण्यासाठी दोन्ही व्हॉल्युम की तीन सेकंद दाबा आणि धरून ठेवा"</string>
<string name="accessibility_button_prompt_text" msgid="1176658502969738564">"तुम्ही अ‍ॅक्सेसिबिलिटी बटण दाबल्यावर वापरण्यासाठी वैशिष्ट्य निवडा:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8259145549733019401">"अ‍ॅक्सेसिबिलिटी जेश्चर ज्या सोबत वापराचे आहे अशी सेवा निवडा (स्क्रीनच्या खालच्या बाजूने दोन बोटांनी स्वाइप करा):"</string>
+ <string name="accessibility_gesture_prompt_text" msgid="8259145549733019401">"अ‍ॅक्सेसिबिलिटी जेश्चर ज्यासोबत वापराचे आहे अशी सेवा निवडा (स्क्रीनच्या खालच्या बाजूने दोन बोटांनी स्वाइप करा):"</string>
<string name="accessibility_gesture_3finger_prompt_text" msgid="1041435574275047665">"अ‍ॅक्सेसिबिलिटी जेश्चर ज्या सोबत वापराचे आहे अशी सेवा निवडा (स्क्रीनच्या खालच्या बाजूने तीन बोटांनी स्वाइप करा):"</string>
<string name="accessibility_button_instructional_text" msgid="7003212763213614833">"सेवांदरम्यान स्विच करण्यासाठी, अ‍ॅक्सेसिबिलिटी बटणाला स्पर्श करा आणि धरून ठेवा."</string>
<string name="accessibility_gesture_instructional_text" msgid="5261788874937410950">"सेवांदरम्यान स्विच करण्यासाठी, दोन बोटांनी वरच्या दिशेला स्वाइप करा आणि धरून ठेवा."</string>
@@ -1891,8 +1891,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"वर्गीकरण न केलेले"</string>
<string name="importance_from_user" msgid="7318955817386549931">"तुम्ही या सूचनांचे महत्त्व सेट केले."</string>
<string name="importance_from_person" msgid="9160133597262938296">"सामील असलेल्या लोकांमुळे हे महत्वाचे आहे."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"<xliff:g id="ACCOUNT">%2$s</xliff:g> सह नवीन वापरकर्ता तयार करण्याची <xliff:g id="APP">%1$s</xliff:g> ला अनुमती द्यायची?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"<xliff:g id="ACCOUNT">%2$s</xliff:g> सह नवीन वापरकर्ता तयार करण्याची (हे खाते असलेला वापरकर्ता आधीपासून विद्यमान आहे) <xliff:g id="APP">%1$s</xliff:g> ला अनुमती द्यायची?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"एक भाषा जोडा"</string>
<string name="country_selection_title" msgid="2954859441620215513">"प्रदेश प्राधान्य"</string>
<string name="search_language_hint" msgid="7042102592055108574">"भाषा नाव टाइप करा"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 1fd1d719fdb0..35e36587ad9c 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -1258,7 +1258,7 @@
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Sambung"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Semua rangkaian"</string>
<string name="wifi_suggestion_title" msgid="6396033039578436801">"Benarkan rangkaian Wi-Fi yang dicadangkan?"</string>
- <string name="wifi_suggestion_content" msgid="5603992011371520746">"<xliff:g id="NAME">%s</xliff:g> rangkaian yang dicadangkan. Peranti mungkin disambungkan secara automatik."</string>
+ <string name="wifi_suggestion_content" msgid="5603992011371520746">"Rangkaian yang dicadangkan oleh <xliff:g id="NAME">%s</xliff:g>. Peranti mungkin disambungkan secara automatik."</string>
<string name="wifi_suggestion_action_allow_app" msgid="7978995387498669901">"Benarkan"</string>
<string name="wifi_suggestion_action_disallow_app" msgid="6434097275967940372">"Tidak perlu"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi akan dihidupkan secara automatik"</string>
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Tidak dikategorikan"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Anda menetapkan kepentingan pemberitahuan ini."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Mesej ini penting disebabkan orang yang terlibat."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Benarkan <xliff:g id="APP">%1$s</xliff:g> membuat Pengguna baharu dengan <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Benarkan <xliff:g id="APP">%1$s</xliff:g> membuat Pengguna baharu dengan <xliff:g id="ACCOUNT">%2$s</xliff:g> (Pengguna dengan akaun ini sudah wujud)?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Tambahkan bahasa"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Pilihan wilayah"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Taipkan nama bahasa"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 4e91ca219225..d69b7d1fcbbb 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -1257,7 +1257,7 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"ကွန်ရက်အားလုံးကို ကြည့်ရန် တို့ပါ"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"ချိတ်ဆက်ရန်"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"ကွန်ရက်အားလုံး"</string>
- <string name="wifi_suggestion_title" msgid="6396033039578436801">"အကြံပြုထားသည့် Wi‑Fi ကွန်ရက်များကို ခွင့်ပြုလိုပါသလား။"</string>
+ <string name="wifi_suggestion_title" msgid="6396033039578436801">"အကြံပြုထားသည့် Wi‑Fi ကွန်ရက်များ ခွင့်ပြုမလား။"</string>
<string name="wifi_suggestion_content" msgid="5603992011371520746">"<xliff:g id="NAME">%s</xliff:g> သည် ကွန်ရက်များကို အကြံပြုထားသည်။ စက်သည် အလိုအလျောက် ချိတ်ဆက်နိုင်သည်။"</string>
<string name="wifi_suggestion_action_allow_app" msgid="7978995387498669901">"ခွင့်ပြုရန်"</string>
<string name="wifi_suggestion_action_disallow_app" msgid="6434097275967940372">"မလိုပါ"</string>
@@ -1891,8 +1891,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"အမျိုးအစားမခွဲရသေးပါ"</string>
<string name="importance_from_user" msgid="7318955817386549931">"ဤသတိပေးချက်များ၏ အရေးပါမှုကိုသတ်မှတ်ပြီးပါပြီ။"</string>
<string name="importance_from_person" msgid="9160133597262938296">"ပါဝင်သည့်လူများကြောင့် အရေးပါပါသည်။"</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"<xliff:g id="APP">%1$s</xliff:g> ကို <xliff:g id="ACCOUNT">%2$s</xliff:g> ဖြင့်အသုံးပြုသူအသစ်ဖန်တီးခွင့်ပြုမလား။"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"<xliff:g id="APP">%1$s</xliff:g> ကို <xliff:g id="ACCOUNT">%2$s</xliff:g> ဖြင့်အသုံးပြုသူအသစ် ဖန်တီးခွင့်ပြုမလား (ဤအကောင့်ဖြင့် အသုံးပြုသူ ရှိနှင့်ပြီးဖြစ်သည်)။"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"ဘာသာစကားတစ်ခု ထည့်ပါ"</string>
<string name="country_selection_title" msgid="2954859441620215513">"ဒေသရွေးချယ်မှု"</string>
<string name="search_language_hint" msgid="7042102592055108574">"ဘာသာစကားအမည် ထည့်ပါ"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 6972a64312c8..a59717047add 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -292,7 +292,7 @@
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Vil du gi &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tilgang til kalenderen din?"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
<string name="permgroupdesc_sms" msgid="4656988620100940350">"sende og lese SMS-meldinger"</string>
- <string name="permgrouprequest_sms" msgid="7168124215838204719">"Vil du la &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; sende og se SMS-meldinger?"</string>
+ <string name="permgrouprequest_sms" msgid="7168124215838204719">"Vil du la &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; sende og se tekstmeldinger?"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"Lagring"</string>
<string name="permgroupdesc_storage" msgid="637758554581589203">"åpne bilder, medieinnhold og filer på enheten din"</string>
<string name="permgrouprequest_storage" msgid="7885942926944299560">"Vil du gi &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tilgang til bilder, medier og filer på enheten din?"</string>
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Uten kategori"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Du angir viktigheten for disse varslene."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Dette er viktig på grunn av folkene som er involvert."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Vil du la <xliff:g id="APP">%1$s</xliff:g> opprette en ny bruker med <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Vil du la <xliff:g id="APP">%1$s</xliff:g> opprette en ny bruker med <xliff:g id="ACCOUNT">%2$s</xliff:g>? (Det finnes allerede en bruker med denne kontoen.)"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Legg til et språk"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Regionsinnstilling"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Skriv inn språknavn"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index ef312a90b205..0cbaaa023c94 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -1896,8 +1896,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"वर्गीकरण नगरिएको"</string>
<string name="importance_from_user" msgid="7318955817386549931">"तपाईंले यी सूचनाहरूको महत्त्व सेट गर्नुहोस् ।"</string>
<string name="importance_from_person" msgid="9160133597262938296">"यसमा सङ्लग्न भएका मानिसहरूको कारणले गर्दा यो महत्वपूर्ण छ।"</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"<xliff:g id="ACCOUNT">%2$s</xliff:g> सँगै नयाँ प्रयोगकर्ता सिर्जना गर्न <xliff:g id="APP">%1$s</xliff:g> लाई अनुमति दिने हो?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"<xliff:g id="ACCOUNT">%2$s</xliff:g> सँगै नयाँ प्रयोगकर्ता सिर्जना गर्न <xliff:g id="APP">%1$s</xliff:g> लाई अनुमति दिने (यस खाताको प्रयोगकर्ता पहिले नै अवस्थित छ) ?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"भाषा थप्नुहोस्"</string>
<string name="country_selection_title" msgid="2954859441620215513">"क्षेत्रको प्राथमिकता"</string>
<string name="search_language_hint" msgid="7042102592055108574">"भाषाको नाम टाइप गर्नुहोस्"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 1a9797bb3709..e898389ed6e7 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Geen categorie"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Je stelt het belang van deze meldingen in."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Dit is belangrijk vanwege de betrokken mensen."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Toestaan dat <xliff:g id="APP">%1$s</xliff:g> een nieuwe gebruiker met <xliff:g id="ACCOUNT">%2$s</xliff:g> maakt?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Toestaan dat <xliff:g id="APP">%1$s</xliff:g> een nieuwe gebruiker met <xliff:g id="ACCOUNT">%2$s</xliff:g> maakt (er is al een gebruiker met dit account)?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Een taal toevoegen"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Regiovoorkeur"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Typ een taalnaam"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 1da68a3c44c4..bd6d2cc8f635 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"ଅବର୍ଗୀକୃତ"</string>
<string name="importance_from_user" msgid="7318955817386549931">"ଏହି ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକର ପ୍ରମୁଖତା ଆପଣ ସେଟ୍‍ କରନ୍ତି।"</string>
<string name="importance_from_person" msgid="9160133597262938296">"ସମ୍ପୃକ୍ତ ଲୋକଙ୍କ କାରଣରୁ ଏହା ଗୁରୁତ୍ୱପୂର୍ଣ୍ଣ ଅଟେ।"</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"<xliff:g id="ACCOUNT">%2$s</xliff:g> ସହ ଏକ ନୂଆ ୟୁଜର୍‌ ତିଆରି କରିବା ପାଇଁ <xliff:g id="APP">%1$s</xliff:g>କୁ ଅନୁମତି ଦେବେ?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"<xliff:g id="ACCOUNT">%2$s</xliff:g> ସହ ଏକ ନୂଆ ୟୁଜର୍‌ ତିଆରି କରିବାକୁ <xliff:g id="APP">%1$s</xliff:g>କୁ ଅନୁମତି ଦେବେ (ଏହି ଆକାଉଣ୍ଟରେ ଜଣେ ୟୁଜର୍‌ ପୂର୍ବରୁ ରହିଛନ୍ତି)?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"ଏକ ଭାଷା ଯୋଡ଼ନ୍ତୁ"</string>
<string name="country_selection_title" msgid="2954859441620215513">"ପସନ୍ଦର ଅଞ୍ଚଳ"</string>
<string name="search_language_hint" msgid="7042102592055108574">"ଭାଷାର ନାମ ଟାଇପ୍‍ କରନ୍ତୁ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index d39ea1354c0f..a9e93d485bfc 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -1891,8 +1891,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"ਗੈਰ-ਸ਼੍ਰੇਣੀਕਿਰਤ"</string>
<string name="importance_from_user" msgid="7318955817386549931">"ਤੁਸੀਂ ਇਹਨਾਂ ਸੂਚਨਾਵਾਂ ਦੀ ਮਹੱਤਤਾ ਸੈੱਟ ਕੀਤੀ।"</string>
<string name="importance_from_person" msgid="9160133597262938296">"ਇਹ ਸ਼ਾਮਲ ਲੋਕਾਂ ਦੇ ਕਾਰਨ ਮਹੱਤਵਪੂਰਨ ਹੈ।"</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"ਕੀ <xliff:g id="APP">%1$s</xliff:g> ਨੂੰ <xliff:g id="ACCOUNT">%2$s</xliff:g> ਨਾਲ ਇੱਕ ਨਵਾਂ ਵਰਤੋਂਕਾਰ ਬਣਾਉਣ ਦੀ ਮਨਜ਼ੂਰੀ ਦੇਣੀ ਹੈ?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"ਕੀ <xliff:g id="APP">%1$s</xliff:g> ਨੂੰ <xliff:g id="ACCOUNT">%2$s</xliff:g> ਨਾਲ ਇੱਕ ਨਵਾਂ ਵਰਤੋਂਕਾਰ ਬਣਾਉਣ ਦੀ ਆਗਿਆ ਦੇਣੀ ਹੈ (ਇਸ ਖਾਤੇ ਨਾਲ ਇੱਕ ਵਰਤੋਂਕਾਰ ਪਹਿਲਾਂ ਤੋਂ ਹੀ ਮੌਜੂਦ ਹੈ) ?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"ਇੱਕ ਭਾਸ਼ਾ ਸ਼ਾਮਲ ਕਰੋ"</string>
<string name="country_selection_title" msgid="2954859441620215513">"ਖੇਤਰ ਤਰਜੀਹ"</string>
<string name="search_language_hint" msgid="7042102592055108574">"ਭਾਸ਼ਾ ਨਾਮ ਟਾਈਪ ਕਰੋ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index a53608a03c24..537089ce640f 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1302,7 +1302,7 @@
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Połącz"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Wszystkie sieci"</string>
<string name="wifi_suggestion_title" msgid="6396033039578436801">"Zezwalać na sugerowane sieci Wi‑Fi?"</string>
- <string name="wifi_suggestion_content" msgid="5603992011371520746">"Sugerowane sieci: <xliff:g id="NAME">%s</xliff:g>. Urządzenie może połączyć się automatycznie."</string>
+ <string name="wifi_suggestion_content" msgid="5603992011371520746">"Sugerowane sieci: <xliff:g id="NAME">%s</xliff:g>. Urządzenie może łączyć się automatycznie."</string>
<string name="wifi_suggestion_action_allow_app" msgid="7978995387498669901">"Zezwól"</string>
<string name="wifi_suggestion_action_disallow_app" msgid="6434097275967940372">"Nie, dziękuję"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi włączy się automatycznie"</string>
@@ -1958,8 +1958,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Bez kategorii"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Ustawiłeś ważność tych powiadomień."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Ta wiadomość jest ważna ze względu na osoby uczestniczące w wątku."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Zezwalasz aplikacji <xliff:g id="APP">%1$s</xliff:g> na utworzenie nowego użytkownika dla konta <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Zezwalasz aplikacji <xliff:g id="APP">%1$s</xliff:g> na utworzenie nowego użytkownika dla konta <xliff:g id="ACCOUNT">%2$s</xliff:g>)? Użytkownik z tym kontem już istnieje."</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Dodaj język"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Ustawienie regionu"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Wpisz nazwę języka"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index f0d8d05e7690..ba8285fa79d9 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -1478,7 +1478,7 @@
<string name="submit" msgid="1602335572089911941">"Enviar"</string>
<string name="car_mode_disable_notification_title" msgid="5704265646471239078">"O app para carro está sendo usado"</string>
<string name="car_mode_disable_notification_message" msgid="7647248420931129377">"Toque para sair do app para carro."</string>
- <string name="tethered_notification_title" msgid="3146694234398202601">"Vínculo ou ponto de acesso ativo"</string>
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Ponto de acesso ou tethering ativo"</string>
<string name="tethered_notification_message" msgid="2113628520792055377">"Toque para configurar."</string>
<string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering desativado"</string>
<string name="disable_tether_notification_message" msgid="2913366428516852495">"Fale com seu administrador para saber detalhes"</string>
@@ -1672,8 +1672,8 @@
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"O atalho de acessibilidade desativou o <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Toque nos dois botões de volume e os mantenha pressionados por três segundo para usar o <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="1176658502969738564">"Escolha um serviço a ser usado quando você toca no botão Acessibilidade:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8259145549733019401">"Escolha um serviço a ser usado com o gesto de acessibilidade (deslizar de baixo para cima na tela com dois dedos):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="1041435574275047665">"Escolha um serviço a ser usado com o gesto de acessibilidade (deslizar de baixo para cima na tela com três dedos):"</string>
+ <string name="accessibility_gesture_prompt_text" msgid="8259145549733019401">"Escolha um serviço para usar com o gesto de acessibilidade (deslizar de baixo para cima na tela com dois dedos):"</string>
+ <string name="accessibility_gesture_3finger_prompt_text" msgid="1041435574275047665">"Escolha um serviço para usar com o gesto de acessibilidade (deslizar de baixo para cima na tela com três dedos):"</string>
<string name="accessibility_button_instructional_text" msgid="7003212763213614833">"Para alternar entre serviços, toque no botão de acessibilidade e mantenha-o pressionado."</string>
<string name="accessibility_gesture_instructional_text" msgid="5261788874937410950">"Para alternar entre serviços, deslize de baixo para cima na tela com dois dedos sem soltar."</string>
<string name="accessibility_gesture_3finger_instructional_text" msgid="4969448938984394550">"Para alternar entre serviços, deslize de baixo para cima na tela com três dedos sem soltar."</string>
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Sem classificação"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Você definiu a importância dessas notificações."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Isso é importante por causa das pessoas envolvidas."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Permitir que <xliff:g id="APP">%1$s</xliff:g> crie um novo usuário com <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Permitir que <xliff:g id="APP">%1$s</xliff:g> crie um novo usuário com <xliff:g id="ACCOUNT">%2$s</xliff:g> (já existe um usuário com essa conta)?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Adicionar um idioma"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Preferência de região"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Digitar nome do idioma"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 5d5618f9b6c1..c7df3a7d848c 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1357,7 +1357,7 @@
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"Acessório de áudio analógico detetado"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"O dispositivo ligado não é compatível com este telemóvel. Toque para saber mais."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Depuração USB ligada"</string>
- <string name="adb_active_notification_message" msgid="7463062450474107752">"Toque para desativar a depuração USB."</string>
+ <string name="adb_active_notification_message" msgid="7463062450474107752">"Toque para desativar a depuração USB"</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Selecione para desativar a depuração por USB."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Modo de estrutura de teste ativado"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Efetue uma reposição de dados de fábrica para desativar o Modo de estrutura de teste."</string>
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Sem categoria"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Definiu a importância destas notificações."</string>
<string name="importance_from_person" msgid="9160133597262938296">"É importante devido às pessoas envolvidas."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Pretende permitir que o <xliff:g id="APP">%1$s</xliff:g> crie um novo utilizador com <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Pretende permitir que o <xliff:g id="APP">%1$s</xliff:g> crie um novo utilizador com <xliff:g id="ACCOUNT">%2$s</xliff:g> (já existe um utilizador com esta conta)?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Adicionar um idioma"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Preferência de região"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Intr. nome do idioma"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index f0d8d05e7690..ba8285fa79d9 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1478,7 +1478,7 @@
<string name="submit" msgid="1602335572089911941">"Enviar"</string>
<string name="car_mode_disable_notification_title" msgid="5704265646471239078">"O app para carro está sendo usado"</string>
<string name="car_mode_disable_notification_message" msgid="7647248420931129377">"Toque para sair do app para carro."</string>
- <string name="tethered_notification_title" msgid="3146694234398202601">"Vínculo ou ponto de acesso ativo"</string>
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Ponto de acesso ou tethering ativo"</string>
<string name="tethered_notification_message" msgid="2113628520792055377">"Toque para configurar."</string>
<string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering desativado"</string>
<string name="disable_tether_notification_message" msgid="2913366428516852495">"Fale com seu administrador para saber detalhes"</string>
@@ -1672,8 +1672,8 @@
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"O atalho de acessibilidade desativou o <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Toque nos dois botões de volume e os mantenha pressionados por três segundo para usar o <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="1176658502969738564">"Escolha um serviço a ser usado quando você toca no botão Acessibilidade:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8259145549733019401">"Escolha um serviço a ser usado com o gesto de acessibilidade (deslizar de baixo para cima na tela com dois dedos):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="1041435574275047665">"Escolha um serviço a ser usado com o gesto de acessibilidade (deslizar de baixo para cima na tela com três dedos):"</string>
+ <string name="accessibility_gesture_prompt_text" msgid="8259145549733019401">"Escolha um serviço para usar com o gesto de acessibilidade (deslizar de baixo para cima na tela com dois dedos):"</string>
+ <string name="accessibility_gesture_3finger_prompt_text" msgid="1041435574275047665">"Escolha um serviço para usar com o gesto de acessibilidade (deslizar de baixo para cima na tela com três dedos):"</string>
<string name="accessibility_button_instructional_text" msgid="7003212763213614833">"Para alternar entre serviços, toque no botão de acessibilidade e mantenha-o pressionado."</string>
<string name="accessibility_gesture_instructional_text" msgid="5261788874937410950">"Para alternar entre serviços, deslize de baixo para cima na tela com dois dedos sem soltar."</string>
<string name="accessibility_gesture_3finger_instructional_text" msgid="4969448938984394550">"Para alternar entre serviços, deslize de baixo para cima na tela com três dedos sem soltar."</string>
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Sem classificação"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Você definiu a importância dessas notificações."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Isso é importante por causa das pessoas envolvidas."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Permitir que <xliff:g id="APP">%1$s</xliff:g> crie um novo usuário com <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Permitir que <xliff:g id="APP">%1$s</xliff:g> crie um novo usuário com <xliff:g id="ACCOUNT">%2$s</xliff:g> (já existe um usuário com essa conta)?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Adicionar um idioma"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Preferência de região"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Digitar nome do idioma"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index d790efc55311..72772288d947 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1924,8 +1924,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Neclasificate"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Dvs. setați importanța acestor notificări."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Notificarea este importantă având în vedere persoanele implicate."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Permiteți ca <xliff:g id="APP">%1$s</xliff:g> să creeze un nou utilizator folosind <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Permiteți ca <xliff:g id="APP">%1$s</xliff:g> să creeze un nou utilizator folosind <xliff:g id="ACCOUNT">%2$s</xliff:g>? (există deja un utilizator cu acest cont)"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Adăugați o limbă"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Regiunea preferată"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Numele limbii"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 0becae0034e3..e458e4644ab2 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1301,7 +1301,7 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Нажмите, чтобы увидеть список сетей"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Подключиться"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Все сети"</string>
- <string name="wifi_suggestion_title" msgid="6396033039578436801">"Разрешить подключение к предложенным сетям Wi‑Fi?"</string>
+ <string name="wifi_suggestion_title" msgid="6396033039578436801">"Подключаться к предложенным сетям Wi‑Fi?"</string>
<string name="wifi_suggestion_content" msgid="5603992011371520746">"Приложение \"<xliff:g id="NAME">%s</xliff:g>\" рекомендует сети, к которым устройство может подключаться автоматически."</string>
<string name="wifi_suggestion_action_allow_app" msgid="7978995387498669901">"Разрешить"</string>
<string name="wifi_suggestion_action_disallow_app" msgid="6434097275967940372">"Нет, спасибо"</string>
@@ -1720,8 +1720,8 @@
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Сервис <xliff:g id="SERVICE_NAME">%1$s</xliff:g> отключен"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Чтобы использовать сервис \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\", нажмите и удерживайте обе клавиши громкости в течение трех секунд."</string>
<string name="accessibility_button_prompt_text" msgid="1176658502969738564">"Выберите сервис, который будет запускаться при нажатии кнопки специальных возможностей:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8259145549733019401">"Выберите сервис, который будет запускаться жестом для доступа к специальным возможностям (провести по экрану снизу вверх двумя пальцами):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="1041435574275047665">"Выберите сервис, который будет запускаться жестом для доступа к специальным возможностям (провести по экрану снизу вверх тремя пальцами):"</string>
+ <string name="accessibility_gesture_prompt_text" msgid="8259145549733019401">"Выберите сервис, который будет запускаться жестом (провести по экрану снизу вверх двумя пальцами):"</string>
+ <string name="accessibility_gesture_3finger_prompt_text" msgid="1041435574275047665">"Выберите сервис, который будет запускаться жестом (провести по экрану снизу вверх тремя пальцами):"</string>
<string name="accessibility_button_instructional_text" msgid="7003212763213614833">"Для переключения между сервисами нажмите и удерживайте кнопку специальных возможностей."</string>
<string name="accessibility_gesture_instructional_text" msgid="5261788874937410950">"Для переключения между сервисами проведите по экрану снизу вверх двумя пальцами и задержите их."</string>
<string name="accessibility_gesture_3finger_instructional_text" msgid="4969448938984394550">"Для переключения между сервисами проведите по экрану снизу вверх тремя пальцами и задержите их."</string>
@@ -1958,8 +1958,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Без категории"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Вы определяете важность этих уведомлений."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Важное (люди)"</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Разрешить приложению \"<xliff:g id="APP">%1$s</xliff:g>\" создать пользователя для аккаунта <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Разрешить приложению \"<xliff:g id="APP">%1$s</xliff:g>\" создать нового пользователя для аккаунта <xliff:g id="ACCOUNT">%2$s</xliff:g> (пользователь c таким аккаунтом уже есть)?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Добавьте язык"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Региональные настройки"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Введите язык"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index a46da16aaa7c..3e0f582ba87e 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -1892,8 +1892,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"වර්ගීකරණය නොකළ"</string>
<string name="importance_from_user" msgid="7318955817386549931">"ඔබ මෙම දැනුම්දීම්වල වැදගත්කම සකසා ඇත."</string>
<string name="importance_from_person" msgid="9160133597262938296">"සම්බන්ධ වූ පුද්ගලයන් නිසා මෙය වැදගත් වේ."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"<xliff:g id="APP">%1$s</xliff:g> හට <xliff:g id="ACCOUNT">%2$s</xliff:g> සමගින් නව පරිශීලකයෙකු සෑදීමට ඉඩ දෙන්නද?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"<xliff:g id="APP">%1$s</xliff:g> හට <xliff:g id="ACCOUNT">%2$s</xliff:g> සමගින් නව පරිශීලකයෙකු සෑදීමට ඉඩ දෙන්නද (මෙම ගිණුම සහිත පරිශීලකයෙකු දැනටමත් සිටී) ?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"භාෂාවක් එක් කරන්න"</string>
<string name="country_selection_title" msgid="2954859441620215513">"ප්‍රදේශ මනාපය"</string>
<string name="search_language_hint" msgid="7042102592055108574">"භාෂා නම ටයිප් කරන්න"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index f9965377bb86..015cd91d60c9 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1302,7 +1302,7 @@
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Pripojiť"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Všetky siete"</string>
<string name="wifi_suggestion_title" msgid="6396033039578436801">"Chcete povoliť navrhované siete Wi‑Fi?"</string>
- <string name="wifi_suggestion_content" msgid="5603992011371520746">"Siete navrhnuté aplikáciou <xliff:g id="NAME">%s</xliff:g>. Zariadenie sa môže pripojiť automaticky."</string>
+ <string name="wifi_suggestion_content" msgid="5603992011371520746">"Siete navrhuje aplikácia <xliff:g id="NAME">%s</xliff:g>. Zariadenie sa môže pripájať automaticky."</string>
<string name="wifi_suggestion_action_allow_app" msgid="7978995387498669901">"Povoliť"</string>
<string name="wifi_suggestion_action_disallow_app" msgid="6434097275967940372">"Nie, ďakujem"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi sa zapne automaticky"</string>
@@ -1958,8 +1958,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Nekategorizované"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Nastavili ste dôležitosť týchto upozornení."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Táto správa je dôležitá vzhľadom na osoby, ktorých sa to týka."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Povoliť aplikácii <xliff:g id="APP">%1$s</xliff:g> vytvoriť nového používateľa pomocou účtu <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Povoliť aplikácii <xliff:g id="APP">%1$s</xliff:g> vytvoriť nového používateľa pomocou účtu <xliff:g id="ACCOUNT">%2$s</xliff:g> (používateľ s týmto účtom už existuje)?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Pridať jazyk"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Preferovaný región"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Zadajte názov jazyka"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index b75644b469e5..54da1dc2d7c8 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1063,10 +1063,10 @@
<item quantity="other">pred <xliff:g id="COUNT_1">%d</xliff:g> minutami</item>
</plurals>
<plurals name="duration_hours_relative" formatted="false" msgid="676894109982008411">
- <item quantity="one">pred <xliff:g id="COUNT_1">%d</xliff:g> uro</item>
- <item quantity="two">pred <xliff:g id="COUNT_1">%d</xliff:g> urama</item>
- <item quantity="few">pred <xliff:g id="COUNT_1">%d</xliff:g> urami</item>
- <item quantity="other">pred <xliff:g id="COUNT_1">%d</xliff:g> urami</item>
+ <item quantity="one">pred <xliff:g id="COUNT_1">%d</xliff:g> h</item>
+ <item quantity="two">pred <xliff:g id="COUNT_1">%d</xliff:g> h</item>
+ <item quantity="few">pred <xliff:g id="COUNT_1">%d</xliff:g> h</item>
+ <item quantity="other">pred <xliff:g id="COUNT_1">%d</xliff:g> h</item>
</plurals>
<plurals name="duration_days_relative" formatted="false" msgid="2203515825765397130">
<item quantity="one">pred <xliff:g id="COUNT_1">%d</xliff:g> dnevom</item>
@@ -1302,7 +1302,7 @@
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Vzpostavi povezavo"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"Vsa omrežja"</string>
<string name="wifi_suggestion_title" msgid="6396033039578436801">"Želite dovoliti predlagana omrežja Wi-Fi?"</string>
- <string name="wifi_suggestion_content" msgid="5603992011371520746">"<xliff:g id="NAME">%s</xliff:g> – predlagana omrežja Naprava se lahko poveže samodejno."</string>
+ <string name="wifi_suggestion_content" msgid="5603992011371520746">"<xliff:g id="NAME">%s</xliff:g> – predlagana omrežja. Naprava se lahko poveže samodejno."</string>
<string name="wifi_suggestion_action_allow_app" msgid="7978995387498669901">"Dovoli"</string>
<string name="wifi_suggestion_action_disallow_app" msgid="6434097275967940372">"Ne, hvala"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Povezava Wi‑Fi se bo samodejno vklopila"</string>
@@ -1539,7 +1539,7 @@
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Končano"</string>
<string name="progress_erasing" msgid="2569962663843586562">"Brisanje skupne shrambe …"</string>
- <string name="share" msgid="1778686618230011964">"Deli z dr."</string>
+ <string name="share" msgid="1778686618230011964">"Deli"</string>
<string name="find" msgid="4808270900322985960">"Najdi"</string>
<string name="websearch" msgid="4337157977400211589">"Spletno iskanje"</string>
<string name="find_next" msgid="5742124618942193978">"Najdi naslednje"</string>
@@ -1958,8 +1958,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Nekategorizirano"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Vi določite raven pomembnosti teh obvestil."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Pomembno zaradi udeleženih ljudi."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Dovolite, da aplikacija <xliff:g id="APP">%1$s</xliff:g> ustvari novega uporabnika za račun <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Dovolite aplikaciji <xliff:g id="APP">%1$s</xliff:g>, da ustvari novega uporabnika za račun <xliff:g id="ACCOUNT">%2$s</xliff:g> (uporabnik s tem računom že obstaja)?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Dodajanje jezika"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Nastavitev območja"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Vnesite ime jezika"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 2b1dd6867553..48ae4d79554e 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -1891,8 +1891,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"E pakategorizuara"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Ke caktuar rëndësinë e këtyre njoftimeve."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Është i rëndësishëm për shkak të personave të përfshirë."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Të lejohet <xliff:g id="APP">%1$s</xliff:g> që të krijojë një përdorues të ri me <xliff:g id="ACCOUNT">%2$s</xliff:g> ?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Të lejohet <xliff:g id="APP">%1$s</xliff:g> që të krijojë një përdorues të ri me <xliff:g id="ACCOUNT">%2$s</xliff:g> (një përdorues me këtë llogari ekziston tashmë) ?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Shto një gjuhë"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Preferenca e rajonit"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Shkruaj emrin e gjuhës"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 6404abc180a4..75fc1ce6732b 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1924,8 +1924,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Некатегоризовано"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Ви подешавате важност ових обавештења."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Ово је важно због људи који учествују."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Желите ли да дозволите апликацији <xliff:g id="APP">%1$s</xliff:g> да направи новог корисника за <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Желите ли да дозволите апликацији <xliff:g id="APP">%1$s</xliff:g> да направи новог корисника за <xliff:g id="ACCOUNT">%2$s</xliff:g> (корисник са овим налогом већ постоји)?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Додајте језик"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Подешавање региона"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Унесите назив језика"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index cf4f786332ae..fdd0b7b7267a 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Okategoriserad"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Du anger hur viktiga aviseringarna är."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Detta är viktigt på grund av personerna som deltar."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Tillåter du att <xliff:g id="APP">%1$s</xliff:g> skapar en ny användare för <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Tillåter du att <xliff:g id="APP">%1$s</xliff:g> skapar en ny användare för <xliff:g id="ACCOUNT">%2$s</xliff:g> (det finns redan en användare med det här kontot)?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Lägg till ett språk"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Regionsinställningar"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Ange språket"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 80fc4f8f78f3..c8c582f334a9 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Ambazo aina haijabainishwa"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Uliweka mipangilio ya umuhimu wa arifa hizi."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Hii ni muhimu kwa sababu ya watu waliohusika."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Ungependa kuruhusu <xliff:g id="APP">%1$s</xliff:g> iunde Mtumiaji mpya ikitumia <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Ungependa kuruhusu <xliff:g id="APP">%1$s</xliff:g> iunde Mtumiaji mpya ikitumia <xliff:g id="ACCOUNT">%2$s</xliff:g> (Je, akaunti hii tayari ina Mtumiaji)?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Ongeza lugha"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Mapendeleo ya eneo"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Weka jina la lugha"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 7028672bd5eb..0956aad3003f 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -341,9 +341,9 @@
<string name="permlab_answerPhoneCalls" msgid="4077162841226223337">"ஃபோன் அழைப்புகளுக்குப் பதிலளி"</string>
<string name="permdesc_answerPhoneCalls" msgid="2901889867993572266">"உள்வரும் ஃபோன் அழைப்பிற்குப் பதிலளிக்க, ஆப்ஸை அனுமதிக்கும்."</string>
<string name="permlab_receiveSms" msgid="8673471768947895082">"உரைச் செய்திகளை (SMS) பெறுதல்"</string>
- <string name="permdesc_receiveSms" msgid="6424387754228766939">"SMS செய்திகளைப் பெற, செயற்படுத்தப் ஆப்ஸை அனுமதிக்கிறது. இதற்கு அர்த்தம் உங்கள் சாதனத்திற்கு அனுப்பப்படும் செய்திகளை உங்களுக்குக் காட்டாமல் கண்காணிப்பதற்கு அல்லது நீக்குவதற்குப் பயன்பாட்டால் முடியும் என்பதாகும்."</string>
+ <string name="permdesc_receiveSms" msgid="6424387754228766939">"SMS செய்திகளைப் பெற, செயற்படுத்தப் ஆப்ஸை அனுமதிக்கிறது. இதற்கு அர்த்தம் உங்கள் சாதனத்திற்கு அனுப்பப்படும் செய்திகளை உங்களுக்குக் காட்டாமல் கண்காணிப்பதற்கு அல்லது நீக்குவதற்கு ஆப்ஸால் முடியும் என்பதாகும்."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"உரைச் செய்திகளை (MMS) பெறுதல்"</string>
- <string name="permdesc_receiveMms" msgid="533019437263212260">"MMS செய்திகளைப் பெற, செயற்படுத்தப் ஆப்ஸை அனுமதிக்கிறது. இதற்கு அர்த்தம் உங்கள் சாதனத்திற்கு அனுப்பப்படும் செய்திகளை உங்களுக்குக் காட்டாமல் கண்காணிக்கவோ, நீக்கவோ பயன்பாட்டால் முடியும் என்பதாகும்."</string>
+ <string name="permdesc_receiveMms" msgid="533019437263212260">"MMS செய்திகளைப் பெற, செயற்படுத்தப் ஆப்ஸை அனுமதிக்கிறது. இதற்கு அர்த்தம் உங்கள் சாதனத்திற்கு அனுப்பப்படும் செய்திகளை உங்களுக்குக் காட்டாமல் கண்காணிக்கவோ, நீக்கவோ ஆப்ஸால் முடியும் என்பதாகும்."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"செல் அலைபரப்புச் செய்திகளைப் படித்தல்"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"உங்கள் சாதனத்தில் பெறப்படும் செல் அலைபரப்புச் செய்திகளைப் படிப்பதற்குப் ஆப்ஸை அனுமதிக்கிறது. அவசரநிலை சூழ்நிலைகளை உங்களுக்கு எச்சரிக்கைச் செய்வதற்கு சில இடங்களில் செல் அலைபரப்பு விழிப்பூட்டல்கள் வழங்கப்படும். அவசரநிலை மொபைல் அலைபரப்புப் பெறப்படும்போது உங்கள் சாதனத்தின் செயல்திறன் அல்லது செயல்பாட்டுடன் தீங்கிழைக்கும் பயன்பாடுகள் அதைத் தடுக்கலாம்."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"குழுசேர்ந்த ஊட்டங்களைப் படித்தல்"</string>
@@ -356,15 +356,15 @@
<string name="permdesc_readSms" product="default" msgid="6826832415656437652">"இந்த ஆப்ஸ் உங்கள் மொபைலில் சேமிக்கப்பட்டுள்ள எல்லா SMS (உரை) செய்திகளையும் படிக்கலாம்."</string>
<string name="permlab_receiveWapPush" msgid="5991398711936590410">"உரைச் செய்திகளைப் (WAP) பெறுதல்"</string>
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"WAP செய்திகளைப் பெற, செயற்படுத்தப் ஆப்ஸை அனுமதிக்கிறது. உங்களுக்கு அனுப்பப்படும் செய்திகளை உங்களுக்குக் காட்டாமல் கண்காணிக்க அல்லது நீக்குவதற்கான திறன் இந்த அனுமதியில் உள்ளடங்கும்."</string>
- <string name="permlab_getTasks" msgid="6466095396623933906">"இயங்கும் பயன்பாடுகளை மீட்டெடுத்தல்"</string>
+ <string name="permlab_getTasks" msgid="6466095396623933906">"இயங்கும் ஆப்ஸை மீட்டெடுத்தல்"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"நடப்பில் மற்றும் சமீபத்தில் இயங்கும் காரியங்களின் தகவலைப் பெற ஆப்ஸை அனுமதிக்கிறது. சாதனத்தில் எந்தப் பயன்பாடுகள் பயன்படுத்தப்படுகின்றன என்பது குறித்த தகவலைக் கண்டறிய ஆப்ஸை இது அனுமதிக்கலாம்."</string>
<string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"சுயவிவரத்தையும் சாதன உரிமையாளர்களையும் நிர்வகித்தல்"</string>
- <string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"சுயவிவர உரிமையாளர்களையும் சாதன உரிமையாளரையும் அமைக்க, பயன்பாடுகளை அனுமதிக்கிறது."</string>
- <string name="permlab_reorderTasks" msgid="2018575526934422779">"இயங்கும் பயன்பாடுகளை மறுவரிசைப்படுத்தல்"</string>
+ <string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"சுயவிவர உரிமையாளர்களையும் சாதன உரிமையாளரையும் அமைக்க, ஆப்ஸை அனுமதிக்கிறது."</string>
+ <string name="permlab_reorderTasks" msgid="2018575526934422779">"இயங்கும் ஆப்ஸை மறுவரிசைப்படுத்தல்"</string>
<string name="permdesc_reorderTasks" msgid="7734217754877439351">"பின்புலத்திலும், முன்புலத்திலும் காரியங்களை நகர்த்த ஆப்ஸை அனுமதிக்கிறது. உங்கள் உள்ளீடு இல்லாமலே ஆப்ஸ் இதைச் செய்யலாம்."</string>
<string name="permlab_enableCarMode" msgid="5684504058192921098">"கார் பயன்முறையை இயக்குதல்"</string>
<string name="permdesc_enableCarMode" msgid="4853187425751419467">"கார் முறையை இயக்க, ஆப்ஸை அனுமதிக்கிறது."</string>
- <string name="permlab_killBackgroundProcesses" msgid="3914026687420177202">"பிற பயன்பாடுகளை மூடுதல்"</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>
@@ -391,13 +391,13 @@
<string name="permdesc_broadcastSticky" product="tv" msgid="5029460344724532288">"வலைபரப்பு முடிந்த பின்னரும் தங்கிவிடும் ஸ்டிக்கி வலைபரப்புகளை அனுப்ப ஆப்ஸை அனுமதிக்கும். அளவுக்கதிகமான உபயோகம் Android TVயின் வேகத்தைக் குறைக்கவோ நிலையற்றதாகவோ ஆக்கக்கூடும். இதனால் அதிகமான நினைவகம் பயன்படுத்தப்படும்."</string>
<string name="permdesc_broadcastSticky" product="default" msgid="2825803764232445091">"அலைபரப்பு முடிந்த பின்னும் இருக்கும், தொடர்ந்து அணுகத்தக்க அலைபரப்பை அனுப்பப் ஆப்ஸை அனுமதிக்கிறது. அதிகமாகப் பயன்படுத்தினால், மொபைலானது நினைவகத்தை மிக அதிகமாகப் பயன்படுத்துவதால் வேகம் குறைந்ததாகவும், நிலையற்றதாகவும் ஆகலாம்."</string>
<string name="permlab_readContacts" msgid="8348481131899886131">"உங்கள் தொடர்புகளைப் படித்தல்"</string>
- <string name="permdesc_readContacts" product="tablet" msgid="5294866856941149639">"குறிப்பிட்டவர்களுடன் நீங்கள் அழைத்த, மின்னஞ்சல் அனுப்பிய அல்லது வேறு வழியில் தொடர்புகொண்டதின் எண்ணிக்கை உட்பட, உங்கள் டேப்லெட்டில் சேமிக்கப்பட்ட உங்கள் தொடர்புகள் குறித்த தரவைப் படிக்க ஆப்ஸை அனுமதிக்கிறது. இந்த அனுமதி, உங்கள் தொடர்பு தரவைச் சேமிக்க பயன்பாடுகளை அனுமதிக்கிறது, மேலும் தீங்கிழைக்கும் பயன்பாடுகள் உங்களுக்குத் தெரியாமல் தொடர்பு தரவைப் பகிரலாம்."</string>
+ <string name="permdesc_readContacts" product="tablet" msgid="5294866856941149639">"குறிப்பிட்டவர்களுடன் நீங்கள் அழைத்த, மின்னஞ்சல் அனுப்பிய அல்லது வேறு வழியில் தொடர்புகொண்டதின் எண்ணிக்கை உட்பட, உங்கள் டேப்லெட்டில் சேமிக்கப்பட்ட உங்கள் தொடர்புகள் குறித்த தரவைப் படிக்க ஆப்ஸை அனுமதிக்கிறது. இந்த அனுமதி, உங்கள் தொடர்பு தரவைச் சேமிக்க ஆப்ஸை அனுமதிக்கிறது, மேலும் தீங்கிழைக்கும் பயன்பாடுகள் உங்களுக்குத் தெரியாமல் தொடர்பு தரவைப் பகிரலாம்."</string>
<string name="permdesc_readContacts" product="tv" msgid="3890061004911027912">"உங்கள் Android TVயில் சேமித்துள்ள தொடர்புகள் பற்றிய தரவைத் தெரிந்துகொள்ள ஆப்ஸை அனுமதிக்கும். குறிப்பிட்ட தனிநபரை எத்தனை முறை அழைத்தீர்கள், பிறவழிகளில் தொடர்புகொண்டீர்கள் அல்லது அவருக்கு எத்தனை முறை மின்னஞ்சல் அனுப்பினீர்கள் என்பதும் இதில் அடங்கும். இது உங்கள் தொடர்புத் தரவைச் சேமிக்க ஆப்ஸை அனுமதிக்கும், அத்துடன் தீங்குவிளைவிக்கும் ஆப்ஸ் உங்களுக்குத் தெரியாமல் தொடர்புத் தரவைப் பகிரக்கூடும்."</string>
- <string name="permdesc_readContacts" product="default" msgid="8440654152457300662">"குறிப்பிட்டவர்களுடன் நீங்கள் அழைத்த, மின்னஞ்சல் அனுப்பிய அல்லது வேறு வழியில் தொடர்புகொண்ட எண்ணிக்கை உட்பட, உங்கள் மொபைலில் சேமிக்கப்பட்ட உங்கள் தொடர்புகள் குறித்த தரவைப் படிக்க ஆப்ஸை அனுமதிக்கிறது. இந்த அனுமதி, உங்கள் தொடர்பு தரவைச் சேமிக்க பயன்பாடுகளை அனுமதிக்கிறது, மேலும் தீங்கிழைக்கும் பயன்பாடுகள் உங்களுக்குத் தெரியாமல் தொடர்பு தரவைப் பகிரலாம்."</string>
+ <string name="permdesc_readContacts" product="default" msgid="8440654152457300662">"குறிப்பிட்டவர்களுடன் நீங்கள் அழைத்த, மின்னஞ்சல் அனுப்பிய அல்லது வேறு வழியில் தொடர்புகொண்ட எண்ணிக்கை உட்பட, உங்கள் மொபைலில் சேமிக்கப்பட்ட உங்கள் தொடர்புகள் குறித்த தரவைப் படிக்க ஆப்ஸை அனுமதிக்கிறது. இந்த அனுமதி, உங்கள் தொடர்பு தரவைச் சேமிக்க ஆப்ஸை அனுமதிக்கிறது, மேலும் தீங்கிழைக்கும் பயன்பாடுகள் உங்களுக்குத் தெரியாமல் தொடர்பு தரவைப் பகிரலாம்."</string>
<string name="permlab_writeContacts" msgid="5107492086416793544">"உங்கள் தொடர்புகளை மாற்றுதல்"</string>
- <string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"குறிப்பிட்ட தொடர்புகளுடன் நீங்கள் அழைத்த, மின்னஞ்சல் அனுப்பிய அல்லது வேறு வழியில் தொடர்புகொண்டதின் எண்ணிக்கை உள்பட, உங்கள் டேப்லெட்டில் சேமிக்கப்பட்ட உங்கள் தொடர்புகள் குறித்த தரவைத் திருத்த ஆப்ஸை அனுமதிக்கிறது. இந்த அனுமதியானது தொடர்புத் தரவை நீக்கப் பயன்பாடுகளை அனுமதிக்கிறது."</string>
+ <string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"குறிப்பிட்ட தொடர்புகளுடன் நீங்கள் அழைத்த, மின்னஞ்சல் அனுப்பிய அல்லது வேறு வழியில் தொடர்புகொண்டதின் எண்ணிக்கை உள்பட, உங்கள் டேப்லெட்டில் சேமிக்கப்பட்ட உங்கள் தொடர்புகள் குறித்த தரவைத் திருத்த ஆப்ஸை அனுமதிக்கிறது. இந்த அனுமதியானது தொடர்புத் தரவை நீக்க ஆப்ஸை அனுமதிக்கிறது."</string>
<string name="permdesc_writeContacts" product="tv" msgid="307929337692573341">"உங்கள் Android TVயில் சேமித்துள்ள தொடர்புகள் பற்றிய தரவை மாற்ற ஆப்ஸை அனுமதிக்கும். குறிப்பிட்ட தொடர்பை எத்தனை முறை அழைத்தீர்கள், பிறவழிகளில் தொடர்புகொண்டீர்கள் அல்லது அவருக்கு எத்தனை முறை மின்னஞ்சல் அனுப்பினீர்கள் என்பதும் இதில் அடங்கும். தொடர்புத் தரவை நீக்க ஆப்ஸை இது அனுமதிக்கும்."</string>
- <string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"குறிப்பிட்ட தொடர்புகளுடன் நீங்கள் அழைத்த, மின்னஞ்சல் அனுப்பிய அல்லது வேறு வழியில் தொடர்புகொண்டதின் எண்ணிக்கை உள்பட, உங்கள் மொபைலில் சேமிக்கப்பட்ட உங்கள் தொடர்புகள் குறித்த தரவைத் திருத்த ஆப்ஸை அனுமதிக்கிறது. இந்த அனுமதியானது தொடர்புத் தரவை நீக்கப் பயன்பாடுகளை அனுமதிக்கிறது."</string>
+ <string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"குறிப்பிட்ட தொடர்புகளுடன் நீங்கள் அழைத்த, மின்னஞ்சல் அனுப்பிய அல்லது வேறு வழியில் தொடர்புகொண்டதின் எண்ணிக்கை உள்பட, உங்கள் மொபைலில் சேமிக்கப்பட்ட உங்கள் தொடர்புகள் குறித்த தரவைத் திருத்த ஆப்ஸை அனுமதிக்கிறது. இந்த அனுமதியானது தொடர்புத் தரவை நீக்க ஆப்ஸை அனுமதிக்கிறது."</string>
<string name="permlab_readCallLog" msgid="3478133184624102739">"அழைப்புப் பதிவைப் படித்தல்"</string>
<string name="permdesc_readCallLog" msgid="3204122446463552146">"இந்த ஆப்ஸ் உங்கள் அழைப்பு வரலாற்றைப் படிக்கலாம்."</string>
<string name="permlab_writeCallLog" msgid="8552045664743499354">"அழைப்புப் பதிவை எழுதுதல்"</string>
@@ -405,7 +405,7 @@
<string name="permdesc_writeCallLog" product="tv" msgid="7939219462637746280">"உள்வரும், வெளிச்செல்லும் அழைப்புகள் குறித்த தகவல் உட்பட உங்கள் Android TVயின் அழைப்புப் பதிவைத் திருத்த ஆப்ஸை அனுமதிக்கும். உங்கள் அழைப்புப் பதிவை அழிக்கவோ திருத்தவோ தீங்கு விளைவிக்கும் ஆப்ஸ் இதைப் பயன்படுத்தக்கூடும்."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"உள்வரும் மற்றும் வெளிச்செல்லும் அழைப்புகள் குறித்த தகவல் உள்பட உங்கள் மொபைல் அழைப்புப் பதிவைத் திருத்துவதற்குப் ஆப்ஸை அனுமதிக்கிறது. உங்கள் அழைப்பின் பதிவை அழிக்க அல்லது திருத்த தீங்கு விளைவிக்கும் பயன்பாடுகள் இதைப் பயன்படுத்தலாம்."</string>
<string name="permlab_bodySensors" msgid="4683341291818520277">"உடல் உணர்விகளை (இதயத் துடிப்பு மானிட்டர்கள் போன்றவை) அணுகுதல்"</string>
- <string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"உங்கள் இதயத்துடிப்பு விகிதம் போன்ற உங்கள் உடல்நிலையைக் கண்காணிக்கும் உணர்விகளில் இருந்து தரவை அணுக பயன்பாடுகளை அனுமதிக்கும்."</string>
+ <string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"உங்கள் இதயத்துடிப்பு விகிதம் போன்ற உங்கள் உடல்நிலையைக் கண்காணிக்கும் உணர்விகளில் இருந்து தரவை அணுக ஆப்ஸை அனுமதிக்கும்."</string>
<string name="permlab_readCalendar" msgid="6716116972752441641">"கேலெண்டர் நிகழ்வுகளையும் விவரங்களையும் படிக்கலாம்"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4993979255403945892">"இந்த ஆப்ஸ் உங்கள் டேப்லெட்டில் சேமிக்கப்பட்டுள்ள கேலெண்டர் நிகழ்வுகள் அனைத்தையும் படிக்கலாம், உங்கள் கேலெண்டர் தரவைப் பகிரலாம் அல்லது சேமிக்கலாம்."</string>
<string name="permdesc_readCalendar" product="tv" msgid="1066881547471014386">"உங்கள் Android TVயில் சேமித்துள்ள அனைத்துக் கேலெண்டர் நிகழ்வுகளையும் இந்த ஆப்ஸால் தெரிந்துகொள்ள முடியும். அத்துடன் உங்களின் கேலெண்டர் தரவைப் பகிரவும் சேமிக்கவும் முடியும்."</string>
@@ -1891,8 +1891,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"வகைப்படுத்தப்படாதவை"</string>
<string name="importance_from_user" msgid="7318955817386549931">"இந்த அறிவிப்புகளின் முக்கியத்துவத்தை அமைத்துள்ளீர்கள்."</string>
<string name="importance_from_person" msgid="9160133597262938296">"ஈடுபட்டுள்ளவர்களின் காரணமாக, இது முக்கியமானது."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"<xliff:g id="ACCOUNT">%2$s</xliff:g> மூலம் புதிய பயனரை உருவாக்க <xliff:g id="APP">%1$s</xliff:g>ஐ அனுமதிக்கவா?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"<xliff:g id="ACCOUNT">%2$s</xliff:g> (இந்தக் கணக்கில் ஏற்கனவே ஒரு பயனர் உள்ளார்) மூலம் புதிய பயனரை உருவாக்க <xliff:g id="APP">%1$s</xliff:g>ஐ அனுமதிக்கவா?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"மொழியைச் சேர்"</string>
<string name="country_selection_title" msgid="2954859441620215513">"மண்டல விருப்பம்"</string>
<string name="search_language_hint" msgid="7042102592055108574">"மொழி பெயரை உள்ளிடுக"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index aa5c271183ae..a8550f860cc9 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -292,7 +292,7 @@
<string name="permgrouprequest_calendar" msgid="289900767793189421">"మీ క్యాలెండర్‌ని యాక్సెస్ చేయడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ని అనుమతించాలా?"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
<string name="permgroupdesc_sms" msgid="4656988620100940350">"SMS సందేశాలను పంపడం మరియు వీక్షించడం"</string>
- <string name="permgrouprequest_sms" msgid="7168124215838204719">"SMS సందేశాలు పంపడానికి మరియు వీక్షించడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ని అనుమతించాలా?"</string>
+ <string name="permgrouprequest_sms" msgid="7168124215838204719">"SMS సందేశాలు పంపడం, చూడటం చేయగలిగేలా &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ను అనుమతించాలా?"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"నిల్వ"</string>
<string name="permgroupdesc_storage" msgid="637758554581589203">"మీ పరికరంలోని ఫోటోలు, మీడియా మరియు ఫైల్‌లను యాక్సెస్ చేయడానికి"</string>
<string name="permgrouprequest_storage" msgid="7885942926944299560">"మీ పరికరంలోని ఫోటోలు, మీడియా మరియు ఫైల్‌లను యాక్సెస్ చేయడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ను అనుమతించాలా?"</string>
@@ -1257,8 +1257,8 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"అన్ని నెట్‌వర్క్‌లు చూడటానికి నొక్కండి"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"కనెక్ట్ చేయి"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"అన్ని నెట్‌వర్క్‌లు"</string>
- <string name="wifi_suggestion_title" msgid="6396033039578436801">"సూచించిన Wi‑Fi నెట్‌వర్క్‍లను అనుమతించాలా?"</string>
- <string name="wifi_suggestion_content" msgid="5603992011371520746">"<xliff:g id="NAME">%s</xliff:g> సూచించిన నెట్ వర్క్ లు పరికరం ఆటోమేటిక్ గా కనెక్ట్ కాకపోవచ్చు."</string>
+ <string name="wifi_suggestion_title" msgid="6396033039578436801">"సూచించిన Wi‑Fi నెట్‌వర్క్‌లను అనుమతించాలా?"</string>
+ <string name="wifi_suggestion_content" msgid="5603992011371520746">"<xliff:g id="NAME">%s</xliff:g> సూచించిన నెట్‌వర్క్‌లు. పరికరం ఆటోమేటిక్‌గా కనెక్ట్ అవచ్చు."</string>
<string name="wifi_suggestion_action_allow_app" msgid="7978995387498669901">"అనుమతించు"</string>
<string name="wifi_suggestion_action_disallow_app" msgid="6434097275967940372">"వద్దు"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi స్వయంచాలకంగా ఆన్ అవుతుంది"</string>
@@ -1673,7 +1673,7 @@
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"యాక్సెస్ సామర్థ్య షార్ట్‌కట్ ద్వారా <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ఆఫ్ చేయబడింది"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>ని ఉపయోగించడానికి వాల్యూమ్ కీలు రెండింటినీ 3 సెకన్లు నొక్కి ఉంచండి"</string>
<string name="accessibility_button_prompt_text" msgid="1176658502969738564">"యాక్సెసిబిలిటీ బటన్‌ను మీరు నొక్కినప్పుడు ఉపయోగించాల్సిన ఒక ఫీచర్‌ను ఎంచుకోండి:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8259145549733019401">"యాక్సెసిబిలిటీ సంజ్ఞతో ఉపయోగించడానికి ఒక సేవను ఎంచుకోండి (రెండు చేతి వేళ్లతో స్క్రీన్‌ను కింద నుండి పైకి స్వైప్ చేయండి):"</string>
+ <string name="accessibility_gesture_prompt_text" msgid="8259145549733019401">"యాక్సెసిబిలిటీ సంజ్ఞతో ఉపయోగించడానికి ఒక సేవను ఎంచుకోండి (రెండు వేళ్లతో స్క్రీన్‌ను కింద నుండి పైకి స్వైప్ చేయండి):"</string>
<string name="accessibility_gesture_3finger_prompt_text" msgid="1041435574275047665">"యాక్సెసిబిలిటీ సంజ్ఞతో ఉపయోగించడానికి ఒక సేవను ఎంచుకోండి (మూడు చేతి వేళ్లతో స్క్రీన్‌ను కింద నుండి పైకి స్వైప్ చేయండి):"</string>
<string name="accessibility_button_instructional_text" msgid="7003212763213614833">"సేవల మధ్య మారడానికి, యాక్సెసిబిలిటీ బటన్‌ను నొక్కి &amp; పట్టుకోండి."</string>
<string name="accessibility_gesture_instructional_text" msgid="5261788874937410950">"సేవల మధ్య మారడానికి, రెండు చేతి వేళ్ళతో పైకి స్వైప్ చేసి పట్టుకోండి."</string>
@@ -1891,8 +1891,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"వర్గీకరించబడలేదు"</string>
<string name="importance_from_user" msgid="7318955817386549931">"మీరు ఈ నోటిఫికేషన్‌ల ప్రాముఖ్యతను సెట్ చేసారు."</string>
<string name="importance_from_person" msgid="9160133597262938296">"ఇందులో పేర్కొనబడిన వ్యక్తులను బట్టి ఇది చాలా ముఖ్యమైనది."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"<xliff:g id="ACCOUNT">%2$s</xliff:g>తో కొత్త వినియోగదారుని సృష్టించడానికి <xliff:g id="APP">%1$s</xliff:g>ని అనుమతించాలా ?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"<xliff:g id="ACCOUNT">%2$s</xliff:g>తో (ఈ ఖాతాతో ఇప్పటికే ఒక వినియోగదారు ఉన్నారు) కొత్త వినియోగదారుని సృష్టించడానికి <xliff:g id="APP">%1$s</xliff:g>ని అనుమతించాలా?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"భాషను జోడించండి"</string>
<string name="country_selection_title" msgid="2954859441620215513">"ప్రాంతం ప్రాధాన్యత"</string>
<string name="search_language_hint" msgid="7042102592055108574">"భాష పేరును టైప్ చేయండి"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 3472cb2a0e9e..f2376d7959d2 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1258,7 +1258,7 @@
<string name="wifi_available_action_connect" msgid="2635699628459488788">"เชื่อมต่อ"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"เครือข่ายทั้งหมด"</string>
<string name="wifi_suggestion_title" msgid="6396033039578436801">"อนุญาตให้เชื่อมต่อเครือข่าย Wi-Fi ที่แนะนำไหม"</string>
- <string name="wifi_suggestion_content" msgid="5603992011371520746">"เครือข่ายที่แนะนำ <xliff:g id="NAME">%s</xliff:g> อุปกรณ์อาจเชื่อมต่อโดยอัตโนมัติ"</string>
+ <string name="wifi_suggestion_content" msgid="5603992011371520746">"เครือข่ายที่แนะนำโดย <xliff:g id="NAME">%s</xliff:g> และอุปกรณ์อาจเชื่อมต่อโดยอัตโนมัติ"</string>
<string name="wifi_suggestion_action_allow_app" msgid="7978995387498669901">"อนุญาต"</string>
<string name="wifi_suggestion_action_disallow_app" msgid="6434097275967940372">"ไม่เป็นไร"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi จะเปิดโดยอัตโนมัติ"</string>
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"ไม่จัดอยู่ในหมวดหมู่ใดๆ"</string>
<string name="importance_from_user" msgid="7318955817386549931">"คุณตั้งค่าความสำคัญของการแจ้งเตือนเหล่านี้"</string>
<string name="importance_from_person" msgid="9160133597262938296">"ข้อความนี้สำคัญเนื่องจากบุคคลที่เกี่ยวข้อง"</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"อนุญาตให้ <xliff:g id="APP">%1$s</xliff:g> สร้างผู้ใช้ใหม่ด้วย <xliff:g id="ACCOUNT">%2$s</xliff:g> ไหม"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"อนุญาตให้ <xliff:g id="APP">%1$s</xliff:g> สร้างผู้ใช้ใหม่ด้วย <xliff:g id="ACCOUNT">%2$s</xliff:g> (มีผู้ใช้ที่มีบัญชีนี้อยู่แล้ว) ไหม"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"เพิ่มภาษา"</string>
<string name="country_selection_title" msgid="2954859441620215513">"ค่ากำหนดภูมิภาค"</string>
<string name="search_language_hint" msgid="7042102592055108574">"พิมพ์ชื่อภาษา"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 49a13c9d8427..9acd72aaeaf0 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Di-nakategorya"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Ikaw ang magtatakda sa kahalagahan ng mga notification na ito."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Mahalaga ito dahil sa mga taong kasangkot."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Payagan ang <xliff:g id="APP">%1$s</xliff:g> na gumawa ng bagong User sa <xliff:g id="ACCOUNT">%2$s</xliff:g> ?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Payagan ang <xliff:g id="APP">%1$s</xliff:g> na gumawa ng bagong User sa <xliff:g id="ACCOUNT">%2$s</xliff:g> (mayroon nang User sa account na ito) ?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Magdagdag ng wika"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Kagustuhan sa rehiyon"</string>
<string name="search_language_hint" msgid="7042102592055108574">"I-type ang wika"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index dd75d2359ee2..e2bda9fc602e 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Kategorize edilmemiş"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Bu bildirimlerin önem derecesini ayarladınız."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Bu, dahil olan kişiler nedeniyle önemlidir."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"<xliff:g id="APP">%1$s</xliff:g> uygulamasının <xliff:g id="ACCOUNT">%2$s</xliff:g> hesabına sahip yeni bir Kullanıcı eklemesine izin verilsin mi?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"<xliff:g id="APP">%1$s</xliff:g> uygulamasının <xliff:g id="ACCOUNT">%2$s</xliff:g> hesabına sahip yeni bir Kullanıcı eklemesine izin verilsin mi (bu hesaba sahip bir kullanıcı zaten var)?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Dil ekleyin"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Bölge tercihi"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Dil adını yazın"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 02c7957af9ab..ee5257554ac9 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1958,8 +1958,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Без категорії"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Ви вказуєте пріоритет цих сповіщень."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Важливе з огляду на учасників."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Дозволити додатку <xliff:g id="APP">%1$s</xliff:g> створити нового користувача з обліковим записом <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Дозволити додатку <xliff:g id="APP">%1$s</xliff:g> створити нового користувача з обліковим записом <xliff:g id="ACCOUNT">%2$s</xliff:g> (користувач із таким обліковим записом уже існує)?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Додати мову"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Вибір регіону"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Введіть назву мови"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index f7d03c50bacb..5cc8db70decc 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -1891,8 +1891,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"غیر زمرہ بند"</string>
<string name="importance_from_user" msgid="7318955817386549931">"ان اطلاعات کی اہمیت آپ مقرر کرتے ہیں۔"</string>
<string name="importance_from_person" msgid="9160133597262938296">"اس میں موجود لوگوں کی وجہ سے یہ اہم ہے۔"</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"<xliff:g id="APP">%1$s</xliff:g> کو <xliff:g id="ACCOUNT">%2$s</xliff:g> کے ساتھ ایک نیا صارف بنانے کی اجازت دیں؟"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"<xliff:g id="APP">%1$s</xliff:g> کو <xliff:g id="ACCOUNT">%2$s</xliff:g> کے ساتھ ایک نیا صارف بنانے کی اجازت دیں (اس اکاؤنٹ کے ساتھ ایک صارف پہلے سے موجود ہے) ؟"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"ایک زبان شامل کریں"</string>
<string name="country_selection_title" msgid="2954859441620215513">"علاقہ کی ترجیح"</string>
<string name="search_language_hint" msgid="7042102592055108574">"زبان کا نام ٹائپ کریں"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index a6c1d3a02e14..cb5191dd82c9 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -1227,12 +1227,12 @@
<string name="volume_music_hint_silent_ringtone_selected" msgid="8310739960973156272">"Ovozsiz rejim tanlandi"</string>
<string name="volume_call" msgid="3941680041282788711">"Suhbat vaqtidagi tovush balandligi"</string>
<string name="volume_bluetooth_call" msgid="2002891926351151534">"Kiruvchi bluetooth tovushi"</string>
- <string name="volume_alarm" msgid="1985191616042689100">"Signal tovushi balandligi"</string>
+ <string name="volume_alarm" msgid="1985191616042689100">"Signal tovushi"</string>
<string name="volume_notification" msgid="2422265656744276715">"Eslatma tovushi"</string>
<string name="volume_unknown" msgid="1400219669770445902">"Tovush balandligi"</string>
<string name="volume_icon_description_bluetooth" msgid="6538894177255964340">"Bluetooth tovushi"</string>
<string name="volume_icon_description_ringer" msgid="3326003847006162496">"Rington balandligi"</string>
- <string name="volume_icon_description_incall" msgid="8890073218154543397">"Qo‘ng‘iroq tovushi balandligi"</string>
+ <string name="volume_icon_description_incall" msgid="8890073218154543397">"Suhbat tovushi"</string>
<string name="volume_icon_description_media" msgid="4217311719665194215">"Multimedia tovushi"</string>
<string name="volume_icon_description_notification" msgid="7044986546477282274">"Eslatma tovushi"</string>
<string name="ringtone_default" msgid="3789758980357696936">"Standart rington"</string>
@@ -1891,8 +1891,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Turkumlanmagan"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Siz ushbu bildirishnomalarning muhimligini belgilagansiz."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Bu odamlar siz uchun muhim."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"<xliff:g id="APP">%1$s</xliff:g> ilovasiga <xliff:g id="ACCOUNT">%2$s</xliff:g> hisobi bilan yangi foydalanuvchi yaratishiga ruxsat berilsinmi ?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"<xliff:g id="APP">%1$s</xliff:g> ilovasiga <xliff:g id="ACCOUNT">%2$s</xliff:g> hisobi bilan yangi foydalanuvchi yaratishiga ruxsat berilsinmi (bunday hisobdagi foydalanuvchi allaqachon mavjud) ?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Til qoʻshish"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Hudud sozlamalari"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Til nomini kiriting"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 59b55d070999..e41dc4374714 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1672,8 +1672,8 @@
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Đã tắt phím tắt trợ năng <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="8376923232350078434">"Nhấn và giữ đồng thời cả hai phím âm lượng trong 3 giây để sử dụng <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="1176658502969738564">"Chọn dịch vụ sẽ sử dụng khi bạn nhấn vào nút hỗ trợ tiếp cận:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8259145549733019401">"Chọn dịch vụ sẽ sử dụng với cử chỉ hỗ trợ tiếp cận (vuốt lên từ cuối màn hình bằng 2 ngón tay):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="1041435574275047665">"Chọn dịch vụ sẽ sử dụng với cử chỉ hỗ trợ tiếp cận (vuốt lên từ cuối màn hình bằng 3 ngón tay):"</string>
+ <string name="accessibility_gesture_prompt_text" msgid="8259145549733019401">"Chọn dịch vụ sẽ sử dụng với cử chỉ hỗ trợ tiếp cận này (vuốt lên từ cuối màn hình bằng 2 ngón tay):"</string>
+ <string name="accessibility_gesture_3finger_prompt_text" msgid="1041435574275047665">"Chọn dịch vụ sẽ sử dụng với cử chỉ hỗ trợ tiếp cận này (vuốt lên từ cuối màn hình bằng 3 ngón tay):"</string>
<string name="accessibility_button_instructional_text" msgid="7003212763213614833">"Để chuyển đổi giữa các dịch vụ, hãy chạm và giữ nút hỗ trợ tiếp cận."</string>
<string name="accessibility_gesture_instructional_text" msgid="5261788874937410950">"Để chuyển đổi giữa các dịch vụ, hãy vuốt lên và giữ bằng 2 ngón tay."</string>
<string name="accessibility_gesture_3finger_instructional_text" msgid="4969448938984394550">"Để chuyển đổi giữa các dịch vụ, hãy vuốt lên và giữ bằng 3 ngón tay."</string>
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Chưa được phân loại"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Bạn đặt tầm quan trọng của các thông báo này."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Thông báo này quan trọng vì những người có liên quan."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Cho phép <xliff:g id="APP">%1$s</xliff:g> tạo người dùng mới bằng <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Cho phép <xliff:g id="APP">%1$s</xliff:g> tạo người dùng mới bằng <xliff:g id="ACCOUNT">%2$s</xliff:g> (người dùng có tài khoản này đã tồn tại)?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Thêm ngôn ngữ"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Tùy chọn khu vực"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Nhập tên ngôn ngữ"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index d8b18d39665a..ba39f1e22e2c 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -283,7 +283,7 @@
<string name="permgrouprequest_contacts" msgid="6032805601881764300">"允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;访问您的通讯录吗?"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"位置信息"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"获取此设备的位置信息"</string>
- <string name="permgrouprequest_location" msgid="3788275734953323491">"允许“<xliff:g id="APP_NAME">%1$s</xliff:g>”获取此设备的位置信息吗?"</string>
+ <string name="permgrouprequest_location" msgid="3788275734953323491">"要允许“<xliff:g id="APP_NAME">%1$s</xliff:g>”获取此设备的位置信息吗?"</string>
<string name="permgrouprequestdetail_location" msgid="1347189607421252902">"只有当您使用该应用时,该应用才有权访问位置信息"</string>
<string name="permgroupbackgroundrequest_location" msgid="5039063878675613235">"要&lt;b&gt;一律允许&lt;/b&gt;允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;访问此设备的位置信息吗?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="4597006851453417387">"目前只有当您使用该应用时,该应用才能访问位置信息"</string>
@@ -292,7 +292,7 @@
<string name="permgrouprequest_calendar" msgid="289900767793189421">"允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;访问您的日历吗?"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"短信"</string>
<string name="permgroupdesc_sms" msgid="4656988620100940350">"发送和查看短信"</string>
- <string name="permgrouprequest_sms" msgid="7168124215838204719">"允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;发送和查看短信吗?"</string>
+ <string name="permgrouprequest_sms" msgid="7168124215838204719">"要允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;发送和查看短信吗?"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"存储空间"</string>
<string name="permgroupdesc_storage" msgid="637758554581589203">"访问您设备上的照片、媒体内容和文件"</string>
<string name="permgrouprequest_storage" msgid="7885942926944299560">"允许“<xliff:g id="APP_NAME">%1$s</xliff:g>”&lt;b&gt;&lt;/b&gt;访问您设备上的照片、媒体内容和文件吗?"</string>
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"未分类"</string>
<string name="importance_from_user" msgid="7318955817386549931">"这些通知的重要程度由您来设置。"</string>
<string name="importance_from_person" msgid="9160133597262938296">"这条通知涉及特定的人,因此被归为重要通知。"</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"允许<xliff:g id="APP">%1$s</xliff:g>使用 <xliff:g id="ACCOUNT">%2$s</xliff:g> 创建新用户吗?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"允许<xliff:g id="APP">%1$s</xliff:g>使用 <xliff:g id="ACCOUNT">%2$s</xliff:g>(目前已有用户使用此帐号)创建新用户吗?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"添加语言"</string>
<string name="country_selection_title" msgid="2954859441620215513">"区域偏好设置"</string>
<string name="search_language_hint" msgid="7042102592055108574">"输入语言名称"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 1a739047630c..f0fce71e9d8c 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"未分類"</string>
<string name="importance_from_user" msgid="7318955817386549931">"您可以設定這些通知的重要性。"</string>
<string name="importance_from_person" msgid="9160133597262938296">"列為重要的原因:涉及的人。"</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"要允許 <xliff:g id="APP">%1$s</xliff:g> 使用 <xliff:g id="ACCOUNT">%2$s</xliff:g> 建立新使用者嗎?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"要允許 <xliff:g id="APP">%1$s</xliff:g> 使用 <xliff:g id="ACCOUNT">%2$s</xliff:g> 建立新使用者 (此帳戶目前已有此使用者) 嗎?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"新增語言"</string>
<string name="country_selection_title" msgid="2954859441620215513">"地區偏好設定"</string>
<string name="search_language_hint" msgid="7042102592055108574">"輸入語言名稱"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 8f8b7580be21..1f132087ede3 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"未分類"</string>
<string name="importance_from_user" msgid="7318955817386549931">"這些通知的重要性由你決定。"</string>
<string name="importance_from_person" msgid="9160133597262938296">"這則通知涉及特定人士,因此被歸為重要通知。"</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"要允許 <xliff:g id="APP">%1$s</xliff:g> 為 <xliff:g id="ACCOUNT">%2$s</xliff:g> 建立新使用者嗎?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"要允許 <xliff:g id="APP">%1$s</xliff:g> 為 <xliff:g id="ACCOUNT">%2$s</xliff:g> 建立新使用者嗎 (這個帳戶目前已有使用者)?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"新增語言"</string>
<string name="country_selection_title" msgid="2954859441620215513">"地區偏好設定"</string>
<string name="search_language_hint" msgid="7042102592055108574">"請輸入語言名稱"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 4c3793374a8c..f707fccef3d9 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1890,8 +1890,10 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Akufakwanga esigabeni"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Usethe ukubaluleka kwalezi zaziso."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Lokhu kubalulekile ngenxa yabantu ababandakanyekayo."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Vumela i-<xliff:g id="APP">%1$s</xliff:g> ukudala umsebenzisi omusha nge-<xliff:g id="ACCOUNT">%2$s</xliff:g> ?"</string>
- <string name="user_creation_adding" msgid="4482658054622099197">"Vumela i-<xliff:g id="APP">%1$s</xliff:g> ukudala umsebenzisi omusha nge-<xliff:g id="ACCOUNT">%2$s</xliff:g> (umsebenzisi onale akhawunti usuvel ukhona) ?"</string>
+ <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
+ <skip />
+ <!-- no translation found for user_creation_adding (9089159170398841763) -->
+ <skip />
<string name="language_selection_title" msgid="2680677278159281088">"Engeza ulwimi"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Okuncamelayo kwesifunda"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Thayipha igama lolimi"</string>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 18d77067e2b3..d2989d9a8ab6 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5015,9 +5015,9 @@
<string name="importance_from_person">This is important because of the people involved.</string>
<!-- Message to user that app trying to create user for an account that already exists. [CHAR LIMIT=none] -->
- <string name="user_creation_account_exists">Allow <xliff:g id="app" example="Gmail">%1$s</xliff:g> to create a new User with <xliff:g id="account" example="foobar@gmail.com">%2$s</xliff:g> ?</string>
+ <string name="user_creation_account_exists">Allow <xliff:g id="app" example="Gmail">%1$s</xliff:g> to create a new User with <xliff:g id="account" example="foobar@gmail.com">%2$s</xliff:g> (a User with this account already exists) ?</string>
<!-- Message to user that app is trying to create user for a specified account. [CHAR LIMIT=none] -->
- <string name="user_creation_adding">Allow <xliff:g id="app" example="Gmail">%1$s</xliff:g> to create a new User with <xliff:g id="account" example="foobar">%2$s</xliff:g> (a User with this account already exists) ?</string>
+ <string name="user_creation_adding">Allow <xliff:g id="app" example="Gmail">%1$s</xliff:g> to create a new User with <xliff:g id="account" example="foobar@gmail.com">%2$s</xliff:g> ?</string>
<!-- Locale picker strings -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 8aa4a8cc01b5..761e02fe5dad 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -206,6 +206,8 @@
<java-symbol type="id" name="action3" />
<java-symbol type="id" name="action4" />
<java-symbol type="id" name="media_seamless" />
+ <java-symbol type="id" name="media_seamless_image" />
+ <java-symbol type="id" name="media_seamless_text" />
<java-symbol type="id" name="notification_media_seekbar_container" />
<java-symbol type="id" name="notification_media_content" />
<java-symbol type="id" name="notification_media_progress" />
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 1670d49a46c4..a4c504b9cbdf 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -43,7 +43,6 @@ android_test {
libs: [
"android.test.runner",
- "telephony-common",
"testables",
"org.apache.http.legacy",
"android.test.base",
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
index 1a22a70eed36..6bce6517a85d 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
@@ -29,6 +29,8 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.util.SparseArray;
+import android.view.Display;
import android.view.View;
import androidx.test.filters.LargeTest;
@@ -51,12 +53,17 @@ import java.util.List;
public class AccessibilityCacheTest {
private static final int WINDOW_ID_1 = 0xBEEF;
private static final int WINDOW_ID_2 = 0xFACE;
+ private static final int WINDOW_ID_3 = 0xABCD;
+ private static final int WINDOW_ID_4 = 0xDCBA;
private static final int SINGLE_VIEW_ID = 0xCAFE;
private static final int OTHER_VIEW_ID = 0xCAB2;
private static final int PARENT_VIEW_ID = 0xFED4;
private static final int CHILD_VIEW_ID = 0xFEED;
private static final int OTHER_CHILD_VIEW_ID = 0xACE2;
private static final int MOCK_CONNECTION_ID = 1;
+ private static final int SECONDARY_DISPLAY_ID = Display.DEFAULT_DISPLAY + 1;
+ private static final int DEFAULT_WINDOW_LAYER = 0;
+ private static final int SPECIFIC_WINDOW_LAYER = 5;
AccessibilityCache mAccessibilityCache;
AccessibilityCache.AccessibilityNodeRefresher mAccessibilityNodeRefresher;
@@ -70,7 +77,7 @@ public class AccessibilityCacheTest {
@After
public void tearDown() {
- // Make sure we're recycling all of our window and node infos
+ // Make sure we're recycling all of our window and node infos.
mAccessibilityCache.clear();
AccessibilityInteractionClient.getInstance().clearCache();
}
@@ -78,7 +85,7 @@ public class AccessibilityCacheTest {
@Test
public void testEmptyCache_returnsNull() {
assertNull(mAccessibilityCache.getNode(0, 0));
- assertNull(mAccessibilityCache.getWindows());
+ assertNull(mAccessibilityCache.getWindowsOnAllDisplays());
assertNull(mAccessibilityCache.getWindow(0));
}
@@ -114,10 +121,11 @@ public class AccessibilityCacheTest {
try {
windowInfo = AccessibilityWindowInfo.obtain();
windowInfo.setId(WINDOW_ID_1);
+ windowInfo.setDisplayId(Display.DEFAULT_DISPLAY);
mAccessibilityCache.addWindow(windowInfo);
// Make a copy
copyOfInfo = AccessibilityWindowInfo.obtain(windowInfo);
- windowInfo.setId(WINDOW_ID_2); // Simulate recycling and reusing the original info
+ windowInfo.setId(WINDOW_ID_2); // Simulate recycling and reusing the original info.
windowFromCache = mAccessibilityCache.getWindow(WINDOW_ID_1);
assertEquals(copyOfInfo, windowFromCache);
} finally {
@@ -129,39 +137,40 @@ public class AccessibilityCacheTest {
@Test
public void addWindowThenClear_noLongerInCache() {
- putWindowWithIdInCache(WINDOW_ID_1);
+ putWindowWithWindowIdAndDisplayIdInCache(WINDOW_ID_1, Display.DEFAULT_DISPLAY,
+ DEFAULT_WINDOW_LAYER);
mAccessibilityCache.clear();
assertNull(mAccessibilityCache.getWindow(WINDOW_ID_1));
}
@Test
public void addWindowGetOtherId_returnsNull() {
- putWindowWithIdInCache(WINDOW_ID_1);
+ putWindowWithWindowIdAndDisplayIdInCache(WINDOW_ID_1, Display.DEFAULT_DISPLAY,
+ DEFAULT_WINDOW_LAYER);
assertNull(mAccessibilityCache.getWindow(WINDOW_ID_1 + 1));
}
@Test
public void addWindowThenGetWindows_returnsNull() {
- putWindowWithIdInCache(WINDOW_ID_1);
- assertNull(mAccessibilityCache.getWindows());
+ putWindowWithWindowIdAndDisplayIdInCache(WINDOW_ID_1, Display.DEFAULT_DISPLAY,
+ DEFAULT_WINDOW_LAYER);
+ assertNull(mAccessibilityCache.getWindowsOnAllDisplays());
}
@Test
public void setWindowsThenGetWindows_returnsInDecreasingLayerOrder() {
- AccessibilityWindowInfo windowInfo1 = null, windowInfo2 = null;
- AccessibilityWindowInfo window1Out = null, window2Out = null;
+ AccessibilityWindowInfo windowInfo1 = null;
+ AccessibilityWindowInfo windowInfo2 = null;
+ AccessibilityWindowInfo window1Out = null;
+ AccessibilityWindowInfo window2Out = null;
List<AccessibilityWindowInfo> windowsOut = null;
try {
- windowInfo1 = AccessibilityWindowInfo.obtain();
- windowInfo1.setId(WINDOW_ID_1);
- windowInfo1.setLayer(5);
- windowInfo2 = AccessibilityWindowInfo.obtain();
- windowInfo2.setId(WINDOW_ID_2);
- windowInfo2.setLayer(windowInfo1.getLayer() + 1);
+ windowInfo1 = obtainAccessibilityWindowInfo(WINDOW_ID_1, SPECIFIC_WINDOW_LAYER);
+ windowInfo2 = obtainAccessibilityWindowInfo(WINDOW_ID_2, windowInfo1.getLayer() + 1);
List<AccessibilityWindowInfo> windowsIn = Arrays.asList(windowInfo1, windowInfo2);
- mAccessibilityCache.setWindows(windowsIn);
+ setWindowsByDisplay(Display.DEFAULT_DISPLAY, windowsIn);
- windowsOut = mAccessibilityCache.getWindows();
+ windowsOut = getWindowsByDisplay(Display.DEFAULT_DISPLAY);
window1Out = mAccessibilityCache.getWindow(WINDOW_ID_1);
window2Out = mAccessibilityCache.getWindow(WINDOW_ID_2);
@@ -182,8 +191,151 @@ public class AccessibilityCacheTest {
}
@Test
+ public void setWindowsAndAddWindow_thenGetWindows_returnsInDecreasingLayerOrder() {
+ AccessibilityWindowInfo windowInfo1 = null;
+ AccessibilityWindowInfo windowInfo2 = null;
+ AccessibilityWindowInfo window1Out = null;
+ AccessibilityWindowInfo window2Out = null;
+ AccessibilityWindowInfo window3Out = null;
+ List<AccessibilityWindowInfo> windowsOut = null;
+ try {
+ windowInfo1 = obtainAccessibilityWindowInfo(WINDOW_ID_1, SPECIFIC_WINDOW_LAYER);
+ windowInfo2 = obtainAccessibilityWindowInfo(WINDOW_ID_2, windowInfo1.getLayer() + 2);
+ List<AccessibilityWindowInfo> windowsIn = Arrays.asList(windowInfo1, windowInfo2);
+ setWindowsByDisplay(Display.DEFAULT_DISPLAY, windowsIn);
+
+ putWindowWithWindowIdAndDisplayIdInCache(WINDOW_ID_3, Display.DEFAULT_DISPLAY,
+ windowInfo1.getLayer() + 1);
+
+ windowsOut = getWindowsByDisplay(Display.DEFAULT_DISPLAY);
+ window1Out = mAccessibilityCache.getWindow(WINDOW_ID_1);
+ window2Out = mAccessibilityCache.getWindow(WINDOW_ID_2);
+ window3Out = mAccessibilityCache.getWindow(WINDOW_ID_3);
+
+ assertEquals(3, windowsOut.size());
+ assertEquals(windowInfo2, windowsOut.get(0));
+ assertEquals(windowInfo1, windowsOut.get(2));
+ assertEquals(windowInfo1, window1Out);
+ assertEquals(windowInfo2, window2Out);
+ assertEquals(window3Out, windowsOut.get(1));
+ } finally {
+ window1Out.recycle();
+ window2Out.recycle();
+ window3Out.recycle();
+ windowInfo1.recycle();
+ windowInfo2.recycle();
+ for (AccessibilityWindowInfo windowInfo : windowsOut) {
+ windowInfo.recycle();
+ }
+ }
+ }
+
+ @Test
+ public void
+ setWindowsAtFirstDisplay_thenAddWindowAtSecondDisplay_returnWindowLayerOrderUnchange() {
+ AccessibilityWindowInfo windowInfo1 = null;
+ AccessibilityWindowInfo windowInfo2 = null;
+ AccessibilityWindowInfo window1Out = null;
+ AccessibilityWindowInfo window2Out = null;
+ List<AccessibilityWindowInfo> windowsOut = null;
+ try {
+ // Sets windows to default display.
+ windowInfo1 = obtainAccessibilityWindowInfo(WINDOW_ID_1, SPECIFIC_WINDOW_LAYER);
+ windowInfo2 = obtainAccessibilityWindowInfo(WINDOW_ID_2, windowInfo1.getLayer() + 2);
+ List<AccessibilityWindowInfo> windowsIn = Arrays.asList(windowInfo1, windowInfo2);
+ setWindowsByDisplay(Display.DEFAULT_DISPLAY, windowsIn);
+ // Adds one window to second display.
+ putWindowWithWindowIdAndDisplayIdInCache(WINDOW_ID_3, SECONDARY_DISPLAY_ID,
+ windowInfo1.getLayer() + 1);
+
+ windowsOut = getWindowsByDisplay(Display.DEFAULT_DISPLAY);
+ window1Out = mAccessibilityCache.getWindow(WINDOW_ID_1);
+ window2Out = mAccessibilityCache.getWindow(WINDOW_ID_2);
+
+ assertEquals(2, windowsOut.size());
+ assertEquals(windowInfo2, windowsOut.get(0));
+ assertEquals(windowInfo1, windowsOut.get(1));
+ assertEquals(windowInfo1, window1Out);
+ assertEquals(windowInfo2, window2Out);
+ } finally {
+ window1Out.recycle();
+ window2Out.recycle();
+ windowInfo1.recycle();
+ windowInfo2.recycle();
+ for (AccessibilityWindowInfo windowInfo : windowsOut) {
+ windowInfo.recycle();
+ }
+ }
+ }
+
+ @Test
+ public void setWindowsAtTwoDisplays_thenGetWindows_returnsInDecreasingLayerOrder() {
+ AccessibilityWindowInfo windowInfo1 = null;
+ AccessibilityWindowInfo windowInfo2 = null;
+ AccessibilityWindowInfo window1Out = null;
+ AccessibilityWindowInfo window2Out = null;
+ AccessibilityWindowInfo windowInfo3 = null;
+ AccessibilityWindowInfo windowInfo4 = null;
+ AccessibilityWindowInfo window3Out = null;
+ AccessibilityWindowInfo window4Out = null;
+ List<AccessibilityWindowInfo> windowsOut1 = null;
+ List<AccessibilityWindowInfo> windowsOut2 = null;
+ try {
+ // Prepares all windows for default display.
+ windowInfo1 = obtainAccessibilityWindowInfo(WINDOW_ID_1, SPECIFIC_WINDOW_LAYER);
+ windowInfo2 = obtainAccessibilityWindowInfo(WINDOW_ID_2, windowInfo1.getLayer() + 1);
+ List<AccessibilityWindowInfo> windowsIn1 = Arrays.asList(windowInfo1, windowInfo2);
+ // Prepares all windows for second display.
+ windowInfo3 = obtainAccessibilityWindowInfo(WINDOW_ID_3, windowInfo1.getLayer() + 2);
+ windowInfo4 = obtainAccessibilityWindowInfo(WINDOW_ID_4, windowInfo1.getLayer() + 3);
+ List<AccessibilityWindowInfo> windowsIn2 = Arrays.asList(windowInfo3, windowInfo4);
+ // Sets all windows of all displays into A11y cache.
+ SparseArray<List<AccessibilityWindowInfo>> allWindows = new SparseArray<>();
+ allWindows.put(Display.DEFAULT_DISPLAY, windowsIn1);
+ allWindows.put(SECONDARY_DISPLAY_ID, windowsIn2);
+ mAccessibilityCache.setWindowsOnAllDisplays(allWindows);
+ // Gets windows at default display.
+ windowsOut1 = getWindowsByDisplay(Display.DEFAULT_DISPLAY);
+ window1Out = mAccessibilityCache.getWindow(WINDOW_ID_1);
+ window2Out = mAccessibilityCache.getWindow(WINDOW_ID_2);
+
+ assertEquals(2, windowsOut1.size());
+ assertEquals(windowInfo2, windowsOut1.get(0));
+ assertEquals(windowInfo1, windowsOut1.get(1));
+ assertEquals(windowInfo1, window1Out);
+ assertEquals(windowInfo2, window2Out);
+ // Gets windows at seocnd display.
+ windowsOut2 = getWindowsByDisplay(SECONDARY_DISPLAY_ID);
+ window3Out = mAccessibilityCache.getWindow(WINDOW_ID_3);
+ window4Out = mAccessibilityCache.getWindow(WINDOW_ID_4);
+
+ assertEquals(2, windowsOut2.size());
+ assertEquals(windowInfo4, windowsOut2.get(0));
+ assertEquals(windowInfo3, windowsOut2.get(1));
+ assertEquals(windowInfo3, window3Out);
+ assertEquals(windowInfo4, window4Out);
+ } finally {
+ window1Out.recycle();
+ window2Out.recycle();
+ windowInfo1.recycle();
+ windowInfo2.recycle();
+ window3Out.recycle();
+ window4Out.recycle();
+ windowInfo3.recycle();
+ windowInfo4.recycle();
+ for (AccessibilityWindowInfo windowInfo : windowsOut1) {
+ windowInfo.recycle();
+ }
+ for (AccessibilityWindowInfo windowInfo : windowsOut2) {
+ windowInfo.recycle();
+ }
+ }
+ }
+
+ @Test
public void addWindowThenStateChangedEvent_noLongerInCache() {
- putWindowWithIdInCache(WINDOW_ID_1);
+ putWindowWithWindowIdAndDisplayIdInCache(WINDOW_ID_1, Display.DEFAULT_DISPLAY,
+ DEFAULT_WINDOW_LAYER);
mAccessibilityCache.onAccessibilityEvent(
AccessibilityEvent.obtain(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED));
assertNull(mAccessibilityCache.getWindow(WINDOW_ID_1));
@@ -191,7 +343,8 @@ public class AccessibilityCacheTest {
@Test
public void addWindowThenWindowsChangedEvent_noLongerInCache() {
- putWindowWithIdInCache(WINDOW_ID_1);
+ putWindowWithWindowIdAndDisplayIdInCache(WINDOW_ID_1, Display.DEFAULT_DISPLAY,
+ DEFAULT_WINDOW_LAYER);
mAccessibilityCache.onAccessibilityEvent(
AccessibilityEvent.obtain(AccessibilityEvent.TYPE_WINDOWS_CHANGED));
assertNull(mAccessibilityCache.getWindow(WINDOW_ID_1));
@@ -622,9 +775,16 @@ public class AccessibilityCacheTest {
}
}
- private void putWindowWithIdInCache(int id) {
+ private AccessibilityWindowInfo obtainAccessibilityWindowInfo(int windowId, int layer) {
AccessibilityWindowInfo windowInfo = AccessibilityWindowInfo.obtain();
- windowInfo.setId(id);
+ windowInfo.setId(windowId);
+ windowInfo.setLayer(layer);
+ return windowInfo;
+ }
+
+ private void putWindowWithWindowIdAndDisplayIdInCache(int windowId, int displayId, int layer) {
+ AccessibilityWindowInfo windowInfo = obtainAccessibilityWindowInfo(windowId, layer);
+ windowInfo.setDisplayId(displayId);
mAccessibilityCache.addWindow(windowInfo);
windowInfo.recycle();
}
@@ -713,4 +873,20 @@ public class AccessibilityCacheTest {
}
}
}
+
+ private void setWindowsByDisplay(int displayId, List<AccessibilityWindowInfo> windows) {
+ SparseArray<List<AccessibilityWindowInfo>> allWindows = new SparseArray<>();
+ allWindows.put(displayId, windows);
+ mAccessibilityCache.setWindowsOnAllDisplays(allWindows);
+ }
+
+ private List<AccessibilityWindowInfo> getWindowsByDisplay(int displayId) {
+ final SparseArray<List<AccessibilityWindowInfo>> allWindows =
+ mAccessibilityCache.getWindowsOnAllDisplays();
+
+ if (allWindows != null && allWindows.size() > 0) {
+ return allWindows.get(displayId);
+ }
+ return null;
+ }
}
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
index 81ce15a4d8d2..c5da54936653 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
@@ -162,6 +162,11 @@ public class ContentCaptureSessionTest {
}
@Override
+ public void internalNotifySessionLifecycle(boolean started) {
+ throw new UnsupportedOperationException("Should not have been called");
+ }
+
+ @Override
public void updateContentCaptureContext(ContentCaptureContext context) {
throw new UnsupportedOperationException("should not have been called");
}
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..cc6eeedaab46 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;
@@ -472,6 +481,36 @@ public class ChooserActivityTest {
}
@Test
+ public void copyTextToClipboardLogging() throws Exception {
+ Intent sendIntent = createSendTextIntent();
+ List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+
+ when(ChooserWrapperActivity.sOverrides.resolverListController.getResolversForIntent(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+
+ MetricsLogger mockLogger = sOverrides.metricsLogger;
+ ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class);
+
+ final ChooserWrapperActivity activity = mActivityRule
+ .launchActivity(Intent.createChooser(sendIntent, null));
+ waitForIdle();
+
+ onView(withId(R.id.copy_button)).check(matches(isDisplayed()));
+ onView(withId(R.id.copy_button)).perform(click());
+
+ verify(mockLogger, atLeastOnce()).write(logMakerCaptor.capture());
+ // First is Activity shown, Second is "with preview"
+ assertThat(logMakerCaptor.getAllValues().get(2).getCategory(),
+ is(MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_SYSTEM_TARGET));
+ assertThat(logMakerCaptor
+ .getAllValues().get(2)
+ .getSubtype(),
+ is(1));
+ }
+
+ @Test
public void oneVisibleImagePreview() throws InterruptedException {
Uri uri = Uri.parse("android.resource://com.android.frameworks.coretests/"
+ com.android.frameworks.coretests.R.drawable.test320x240);
@@ -770,6 +809,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 +972,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 +1038,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 +1099,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 +1238,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/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index e6eaa6964d49..96ac0f9b38b5 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -207,7 +207,7 @@ public class GradientDrawable extends Drawable {
}
public GradientDrawable() {
- this(new GradientState(Orientation.LEFT_RIGHT, null), null);
+ this(new GradientState(Orientation.TOP_BOTTOM, null), null);
}
/**
@@ -637,7 +637,7 @@ public class GradientDrawable extends Drawable {
* @see #setOrientation(Orientation)
*/
public Orientation getOrientation() {
- return mGradientState.getOrientation();
+ return mGradientState.mOrientation;
}
/**
@@ -653,7 +653,7 @@ public class GradientDrawable extends Drawable {
* @see #getOrientation()
*/
public void setOrientation(Orientation orientation) {
- mGradientState.setOrientation(orientation);
+ mGradientState.mOrientation = orientation;
mGradientIsDirty = true;
invalidateSelf();
}
@@ -1270,7 +1270,7 @@ public class GradientDrawable extends Drawable {
if (st.mGradient == LINEAR_GRADIENT) {
final float level = st.mUseLevel ? getLevel() / 10000.0f : 1.0f;
- switch (st.getOrientation()) {
+ switch (st.mOrientation) {
case TOP_BOTTOM:
x0 = r.left; y0 = r.top;
x1 = x0; y1 = level * r.bottom;
@@ -1759,6 +1759,33 @@ public class GradientDrawable extends Drawable {
int angle = (int) a.getFloat(R.styleable.GradientDrawableGradient_angle, st.mAngle);
st.mAngle = ((angle % 360) + 360) % 360; // offset negative angle measures
+ switch (st.mAngle) {
+ case 0:
+ st.mOrientation = Orientation.LEFT_RIGHT;
+ break;
+ case 45:
+ st.mOrientation = Orientation.BL_TR;
+ break;
+ case 90:
+ st.mOrientation = Orientation.BOTTOM_TOP;
+ break;
+ case 135:
+ st.mOrientation = Orientation.BR_TL;
+ break;
+ case 180:
+ st.mOrientation = Orientation.RIGHT_LEFT;
+ break;
+ case 225:
+ st.mOrientation = Orientation.TR_BL;
+ break;
+ case 270:
+ st.mOrientation = Orientation.TOP_BOTTOM;
+ break;
+ case 315:
+ st.mOrientation = Orientation.TL_BR;
+ break;
+ }
+
final TypedValue tv = a.peekValue(R.styleable.GradientDrawableGradient_gradientRadius);
if (tv != null) {
final float radius;
@@ -1981,7 +2008,7 @@ public class GradientDrawable extends Drawable {
int[] mAttrPadding;
public GradientState(Orientation orientation, int[] gradientColors) {
- setOrientation(orientation);
+ mOrientation = orientation;
setGradientColors(gradientColors);
}
@@ -2184,93 +2211,11 @@ public class GradientDrawable extends Drawable {
mCenterY = y;
}
- public void setOrientation(Orientation orientation) {
- // Update the angle here so that subsequent attempts to obtain the orientation
- // from the angle overwrite previously configured values during inflation
- mAngle = getAngleFromOrientation(orientation);
- mOrientation = orientation;
- }
-
@NonNull
public Orientation getOrientation() {
- updateGradientStateOrientation();
return mOrientation;
}
- /**
- * Update the orientation of the gradient based on the given angle only if the type is
- * {@link #LINEAR_GRADIENT}
- */
- private void updateGradientStateOrientation() {
- if (mGradient == LINEAR_GRADIENT) {
- int angle = mAngle;
- if (angle % 45 != 0) {
- throw new IllegalArgumentException("Linear gradient requires 'angle' attribute "
- + "to be a multiple of 45");
- }
-
- Orientation orientation;
- switch (angle) {
- case 0:
- orientation = Orientation.LEFT_RIGHT;
- break;
- case 45:
- orientation = Orientation.BL_TR;
- break;
- case 90:
- orientation = Orientation.BOTTOM_TOP;
- break;
- case 135:
- orientation = Orientation.BR_TL;
- break;
- case 180:
- orientation = Orientation.RIGHT_LEFT;
- break;
- case 225:
- orientation = Orientation.TR_BL;
- break;
- case 270:
- orientation = Orientation.TOP_BOTTOM;
- break;
- case 315:
- orientation = Orientation.TL_BR;
- break;
- default:
- // Should not get here as exception is thrown above if angle is not multiple
- // of 45 degrees
- orientation = Orientation.LEFT_RIGHT;
- break;
- }
- mOrientation = orientation;
- }
- }
-
- private int getAngleFromOrientation(@Nullable Orientation orientation) {
- if (orientation != null) {
- switch (orientation) {
- default:
- case LEFT_RIGHT:
- return 0;
- case BL_TR:
- return 45;
- case BOTTOM_TOP:
- return 90;
- case BR_TL:
- return 135;
- case RIGHT_LEFT:
- return 180;
- case TR_BL:
- return 225;
- case TOP_BOTTOM:
- return 270;
- case TL_BR:
- return 315;
- }
- } else {
- return 0;
- }
- }
-
public void setGradientColors(@Nullable int[] colors) {
mGradientColors = colors;
mSolidColors = null;
diff --git a/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java
index 08f417662523..54995ac9d050 100644
--- a/keystore/java/android/security/Credentials.java
+++ b/keystore/java/android/security/Credentials.java
@@ -16,11 +16,12 @@
package android.security;
+import android.annotation.UnsupportedAppUsage;
+
import com.android.org.bouncycastle.util.io.pem.PemObject;
import com.android.org.bouncycastle.util.io.pem.PemReader;
import com.android.org.bouncycastle.util.io.pem.PemWriter;
-import android.annotation.UnsupportedAppUsage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -90,9 +91,9 @@ public class Credentials {
public static final String EXTRA_INSTALL_AS_UID = "install_as_uid";
/**
- * Intent extra: name for the user's private key.
+ * Intent extra: name for the user's key pair.
*/
- public static final String EXTRA_USER_PRIVATE_KEY_NAME = "user_private_key_name";
+ public static final String EXTRA_USER_KEY_ALIAS = "user_key_pair_name";
/**
* Intent extra: data for the user's private key in PEM-encoded PKCS#8.
@@ -100,21 +101,11 @@ public class Credentials {
public static final String EXTRA_USER_PRIVATE_KEY_DATA = "user_private_key_data";
/**
- * Intent extra: name for the user's certificate.
- */
- public static final String EXTRA_USER_CERTIFICATE_NAME = "user_certificate_name";
-
- /**
* Intent extra: data for the user's certificate in PEM-encoded X.509.
*/
public static final String EXTRA_USER_CERTIFICATE_DATA = "user_certificate_data";
/**
- * Intent extra: name for CA certificate chain
- */
- public static final String EXTRA_CA_CERTIFICATES_NAME = "ca_certificates_name";
-
- /**
* Intent extra: data for CA certificate chain in PEM-encoded X.509.
*/
public static final String EXTRA_CA_CERTIFICATES_DATA = "ca_certificates_data";
diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl
index b3cdff7eedf7..97da3cc6f80f 100644
--- a/keystore/java/android/security/IKeyChainService.aidl
+++ b/keystore/java/android/security/IKeyChainService.aidl
@@ -43,7 +43,8 @@ interface IKeyChainService {
String installCaCertificate(in byte[] caCertificate);
// APIs used by DevicePolicyManager
- boolean installKeyPair(in byte[] privateKey, in byte[] userCert, in byte[] certChain, String alias);
+ boolean installKeyPair(
+ in byte[] privateKey, in byte[] userCert, in byte[] certChain, String alias, int uid);
boolean removeKeyPair(String alias);
// APIs used by Settings
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 67c181b452bb..3010206cdc5b 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -569,6 +569,7 @@ void SkiaPipeline::renderOverdraw(const LayerUpdateQueue& layers, const SkRect&
// Set up the overdraw canvas.
SkImageInfo offscreenInfo = SkImageInfo::MakeA8(surface->width(), surface->height());
sk_sp<SkSurface> offscreen = surface->makeSurface(offscreenInfo);
+ LOG_ALWAYS_FATAL_IF(!offscreen, "Failed to create offscreen SkSurface for overdraw viz.");
SkOverdrawCanvas overdrawCanvas(offscreen->getCanvas());
// Fake a redraw to replay the draw commands. This will increment the alpha channel
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index b5ef8f43dfb6..684dc22ee4d1 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -146,7 +146,7 @@ void CanvasContext::setSurface(sp<Surface>&& surface) {
if (surface) {
mNativeSurface = new ReliableSurface{std::move(surface)};
// TODO: Fix error handling & re-shorten timeout
- mNativeSurface->setDequeueTimeout(4000_ms);
+ ANativeWindow_setDequeueTimeout(mNativeSurface.get(), 4000_ms);
mNativeSurface->enableFrameTimestamps(true);
} else {
mNativeSurface = nullptr;
diff --git a/libs/hwui/renderthread/ReliableSurface.h b/libs/hwui/renderthread/ReliableSurface.h
index 0d251b1f10e7..f768df37ba7d 100644
--- a/libs/hwui/renderthread/ReliableSurface.h
+++ b/libs/hwui/renderthread/ReliableSurface.h
@@ -31,8 +31,6 @@ public:
ReliableSurface(sp<Surface>&& surface);
~ReliableSurface();
- void setDequeueTimeout(nsecs_t timeout) { mSurface->setDequeueTimeout(timeout); }
-
int reserveNext();
void allocateBuffers() { mSurface->allocateBuffers(); }
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/Android.bp b/location/lib/Android.bp
index ff6921d3e1c2..b36aa036daba 100644
--- a/location/lib/Android.bp
+++ b/location/lib/Android.bp
@@ -18,7 +18,7 @@ java_sdk_library {
name: "com.android.location.provider",
srcs: [
"java/**/*.java",
- ":framework-srcs",
+ ":framework-all-sources",
],
libs: [
"androidx.annotation_annotation",
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/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 55583d58e0c2..5b535651abd9 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -62,6 +62,7 @@ import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
@@ -1451,6 +1452,37 @@ public class ExifInterface {
}
/**
+ * Returns whether ExifInterface currently supports parsing data from the specified mime type
+ * or not.
+ *
+ * @param mimeType the string value of mime type
+ */
+ public static boolean isSupportedMimeType(@NonNull String mimeType) {
+ if (mimeType == null) {
+ throw new NullPointerException("mimeType shouldn't be null");
+ }
+
+ switch (mimeType.toLowerCase(Locale.ROOT)) {
+ case "image/jpeg":
+ case "image/x-adobe-dng":
+ case "image/x-canon-cr2":
+ case "image/x-nikon-nef":
+ case "image/x-nikon-nrw":
+ case "image/x-sony-arw":
+ case "image/x-panasonic-rw2":
+ case "image/x-olympus-orf":
+ case "image/x-pentax-pef":
+ case "image/x-samsung-srw":
+ case "image/x-fuji-raf":
+ case "image/heic":
+ case "image/heif":
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
* Returns the EXIF attribute of the specified tag or {@code null} if there is no such tag in
* the image file.
*
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index cbc820b6fde0..05aaa82f8ac8 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -745,6 +745,8 @@ status_t JMediaCodec::getCodecInfo(JNIEnv *env, jobject *codecInfoObject) const
status_t JMediaCodec::getMetrics(JNIEnv *, MediaAnalyticsItem * &reply) const {
mediametrics_handle_t reply2 = MediaAnalyticsItem::convert(reply);
status_t status = mCodec->getMetrics(reply2);
+ // getMetrics() updates reply2, pass the converted update along to our caller.
+ reply = MediaAnalyticsItem::convert(reply2);
return status;
}
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.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/lib/signer/Android.bp b/media/lib/signer/Android.bp
index 85a007f427b8..2286c5379e1a 100644
--- a/media/lib/signer/Android.bp
+++ b/media/lib/signer/Android.bp
@@ -18,7 +18,7 @@ java_sdk_library {
name: "com.android.mediadrm.signer",
srcs: [
"java/**/*.java",
- ":framework-srcs",
+ ":framework-all-sources",
],
api_packages: ["com.android.mediadrm.signer"],
}
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/core/proto/android/server/backup_chunks_metadata.proto b/packages/BackupEncryption/proto/backup_chunks_metadata.proto
index a375f02545c5..2fdedbf70975 100644
--- a/core/proto/android/server/backup_chunks_metadata.proto
+++ b/packages/BackupEncryption/proto/backup_chunks_metadata.proto
@@ -15,8 +15,10 @@
*/
syntax = "proto2";
-package com.android.server.backup.encryption.chunk;
+package android_backup_crypto;
+
+option java_package = "com.android.server.backup.encryption.protos";
option java_outer_classname = "ChunksMetadataProto";
// Cipher type with which the chunks are encrypted. For now we only support AES/GCM/NoPadding, but
diff --git a/packages/BackupEncryption/proto/key_value_listing.proto b/packages/BackupEncryption/proto/key_value_listing.proto
new file mode 100644
index 000000000000..001e697bd804
--- /dev/null
+++ b/packages/BackupEncryption/proto/key_value_listing.proto
@@ -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
+ */
+
+syntax = "proto2";
+
+package android_backup_crypto;
+
+option java_package = "com.android.server.backup.encryption.protos";
+option java_outer_classname = "KeyValueListingProto";
+
+// An entry of a key-value pair.
+message KeyValueEntry {
+ // Plaintext key of the key-value pair.
+ optional string key = 1;
+ // SHA-256 MAC of the plaintext of the chunk containing the pair
+ optional bytes hash = 2;
+}
+
+// Describes the key/value pairs currently in the backup blob, mapping from the
+// plaintext key to the hash of the chunk containing the pair.
+//
+// This is local state stored on the device. It is never sent to the
+// backup server. See ChunkOrdering for how the device restores the
+// key-value pairs in the correct order.
+message KeyValueListing {
+ repeated KeyValueEntry entries = 1;
+}
diff --git a/packages/BackupEncryption/proto/key_value_pair.proto b/packages/BackupEncryption/proto/key_value_pair.proto
new file mode 100644
index 000000000000..177fa3025dc8
--- /dev/null
+++ b/packages/BackupEncryption/proto/key_value_pair.proto
@@ -0,0 +1,31 @@
+/*
+ * 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
+ */
+
+syntax = "proto2";
+
+package android_backup_crypto;
+
+option java_package = "com.android.server.backup.encryption.protos";
+option java_outer_classname = "KeyValuePairProto";
+
+// Serialized form of a key-value pair, when it is to be encrypted in a blob.
+// The backup blob for a key-value database consists of repeated encrypted
+// key-value pairs like this, in a randomized order. See ChunkOrdering for how
+// these are then reconstructed during a restore.
+message KeyValuePair {
+ optional string key = 1;
+ optional bytes value = 2;
+}
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/chunk/Chunk.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/Chunk.java
deleted file mode 100644
index ba328609a77e..000000000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/Chunk.java
+++ /dev/null
@@ -1,70 +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.server.backup.encryption.chunk;
-
-import android.util.proto.ProtoInputStream;
-
-import java.io.IOException;
-
-/**
- * Information about a chunk entry in a protobuf. Only used for reading from a {@link
- * ProtoInputStream}.
- */
-public class Chunk {
- /**
- * Reads a Chunk from a {@link ProtoInputStream}. Expects the message to be of format {@link
- * ChunksMetadataProto.Chunk}.
- *
- * @param inputStream currently at a {@link ChunksMetadataProto.Chunk} message.
- * @throws IOException when the message is not structured as expected or a field can not be
- * read.
- */
- static Chunk readFromProto(ProtoInputStream inputStream) throws IOException {
- Chunk result = new Chunk();
-
- while (inputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
- switch (inputStream.getFieldNumber()) {
- case (int) ChunksMetadataProto.Chunk.HASH:
- result.mHash = inputStream.readBytes(ChunksMetadataProto.Chunk.HASH);
- break;
- case (int) ChunksMetadataProto.Chunk.LENGTH:
- result.mLength = inputStream.readInt(ChunksMetadataProto.Chunk.LENGTH);
- break;
- }
- }
-
- return result;
- }
-
- private int mLength;
- private byte[] mHash;
-
- /** Private constructor. This class should only be instantiated by calling readFromProto. */
- private Chunk() {
- // Set default values for fields in case they are not available in the proto.
- mHash = new byte[]{};
- mLength = 0;
- }
-
- public int getLength() {
- return mLength;
- }
-
- public byte[] getHash() {
- return mHash;
- }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/ChunkListingMap.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/ChunkListingMap.java
index a44890118717..51d7d532c920 100644
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/ChunkListingMap.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/ChunkListingMap.java
@@ -17,51 +17,41 @@
package com.android.server.backup.encryption.chunk;
import android.annotation.Nullable;
-import android.util.proto.ProtoInputStream;
-import java.io.IOException;
-import java.util.Collections;
+import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
+
import java.util.HashMap;
import java.util.Map;
/**
- * Chunk listing in a format optimized for quick look-up of chunks via their hash keys. This is
+ * Chunk listing in a format optimized for quick look up of chunks via their hash keys. This is
* useful when building an incremental backup. After a chunk has been produced, the algorithm can
* quickly look up whether the chunk existed in the previous backup by checking this chunk listing.
* It can then tell the server to use that chunk, through telling it the position and length of the
* chunk in the previous backup's blob.
*/
public class ChunkListingMap {
- /**
- * Reads a ChunkListingMap from a {@link ProtoInputStream}. Expects the message to be of format
- * {@link ChunksMetadataProto.ChunkListing}.
- *
- * @param inputStream Currently at a {@link ChunksMetadataProto.ChunkListing} message.
- * @throws IOException when the message is not structured as expected or a field can not be
- * read.
- */
- public static ChunkListingMap readFromProto(ProtoInputStream inputStream) throws IOException {
- Map<ChunkHash, Entry> entries = new HashMap();
+
+ private final Map<ChunkHash, Entry> mChunksByHash;
+
+ /** Construct a map from a {@link ChunksMetadataProto.ChunkListing} protobuf */
+ public static ChunkListingMap fromProto(ChunksMetadataProto.ChunkListing chunkListingProto) {
+ Map<ChunkHash, Entry> entries = new HashMap<>();
long start = 0;
- while (inputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
- if (inputStream.getFieldNumber() == (int) ChunksMetadataProto.ChunkListing.CHUNKS) {
- long chunkToken = inputStream.start(ChunksMetadataProto.ChunkListing.CHUNKS);
- Chunk chunk = Chunk.readFromProto(inputStream);
- entries.put(new ChunkHash(chunk.getHash()), new Entry(start, chunk.getLength()));
- start += chunk.getLength();
- inputStream.end(chunkToken);
- }
+ for (ChunksMetadataProto.Chunk chunk : chunkListingProto.chunks) {
+ entries.put(new ChunkHash(chunk.hash), new Entry(start, chunk.length));
+ start += chunk.length;
}
return new ChunkListingMap(entries);
}
- private final Map<ChunkHash, Entry> mChunksByHash;
-
private ChunkListingMap(Map<ChunkHash, Entry> chunksByHash) {
- mChunksByHash = Collections.unmodifiableMap(new HashMap<>(chunksByHash));
+ // This is only called from the {@link #fromProto} method, so we don't
+ // need to take a copy.
+ this.mChunksByHash = chunksByHash;
}
/** Returns {@code true} if there is a chunk with the given SHA-256 MAC key in the listing. */
@@ -81,19 +71,14 @@ public class ChunkListingMap {
return mChunksByHash.get(hash);
}
- /** Returns the number of chunks in this listing. */
- public int getChunkCount() {
- return mChunksByHash.size();
- }
-
/** Information about a chunk entry in a backup blob - i.e., its position and length. */
public static final class Entry {
private final int mLength;
private final long mStart;
private Entry(long start, int length) {
- mStart = start;
mLength = length;
+ mStart = start;
}
/** Returns the length of the chunk in bytes. */
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/ChunkOrderingType.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/ChunkOrderingType.java
index 8cb028e46e9d..9cda3395f79a 100644
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/ChunkOrderingType.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/ChunkOrderingType.java
@@ -16,9 +16,9 @@
package com.android.server.backup.encryption.chunk;
-import static com.android.server.backup.encryption.chunk.ChunksMetadataProto.CHUNK_ORDERING_TYPE_UNSPECIFIED;
-import static com.android.server.backup.encryption.chunk.ChunksMetadataProto.EXPLICIT_STARTS;
-import static com.android.server.backup.encryption.chunk.ChunksMetadataProto.INLINE_LENGTHS;
+import static com.android.server.backup.encryption.protos.nano.ChunksMetadataProto.CHUNK_ORDERING_TYPE_UNSPECIFIED;
+import static com.android.server.backup.encryption.protos.nano.ChunksMetadataProto.EXPLICIT_STARTS;
+import static com.android.server.backup.encryption.protos.nano.ChunksMetadataProto.INLINE_LENGTHS;
import android.annotation.IntDef;
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoder.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoder.java
index 7b38dd4a1dc3..6b9be9fd91d3 100644
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoder.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoder.java
@@ -17,7 +17,7 @@
package com.android.server.backup.encryption.chunking;
import com.android.server.backup.encryption.chunk.ChunkOrderingType;
-import com.android.server.backup.encryption.chunk.ChunksMetadataProto;
+import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
import java.io.IOException;
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoder.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoder.java
index 567f75d59513..e707350505fb 100644
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoder.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoder.java
@@ -17,7 +17,7 @@
package com.android.server.backup.encryption.chunking;
import com.android.server.backup.encryption.chunk.ChunkOrderingType;
-import com.android.server.backup.encryption.chunk.ChunksMetadataProto;
+import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
import java.io.IOException;
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/kv/DecryptedChunkKvOutput.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/kv/DecryptedChunkKvOutput.java
new file mode 100644
index 000000000000..56e1c053d8e3
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/kv/DecryptedChunkKvOutput.java
@@ -0,0 +1,111 @@
+/*
+ * 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.kv;
+
+import static com.android.internal.util.Preconditions.checkState;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.backup.encryption.chunk.ChunkHash;
+import com.android.server.backup.encryption.chunking.ChunkHasher;
+import com.android.server.backup.encryption.protos.nano.KeyValuePairProto;
+import com.android.server.backup.encryption.tasks.DecryptedChunkOutput;
+
+import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Builds a key value backup set from plaintext chunks. Computes a digest over the sorted SHA-256
+ * hashes of the chunks.
+ */
+public class DecryptedChunkKvOutput implements DecryptedChunkOutput {
+ @VisibleForTesting static final String DIGEST_ALGORITHM = "SHA-256";
+
+ private final ChunkHasher mChunkHasher;
+ private final List<KeyValuePairProto.KeyValuePair> mUnsortedPairs = new ArrayList<>();
+ private final List<ChunkHash> mUnsortedHashes = new ArrayList<>();
+ private boolean mClosed;
+
+ /** Constructs a new instance which computers the digest using the given hasher. */
+ public DecryptedChunkKvOutput(ChunkHasher chunkHasher) {
+ mChunkHasher = chunkHasher;
+ }
+
+ @Override
+ public DecryptedChunkOutput open() {
+ // As we don't have any resources there is nothing to open.
+ return this;
+ }
+
+ @Override
+ public void processChunk(byte[] plaintextBuffer, int length)
+ throws IOException, InvalidKeyException {
+ checkState(!mClosed, "Cannot process chunk after close()");
+ KeyValuePairProto.KeyValuePair kvPair = new KeyValuePairProto.KeyValuePair();
+ KeyValuePairProto.KeyValuePair.mergeFrom(kvPair, plaintextBuffer, 0, length);
+ mUnsortedPairs.add(kvPair);
+ // TODO(b/71492289): Update ChunkHasher to accept offset and length so we don't have to copy
+ // the buffer into a smaller array.
+ mUnsortedHashes.add(mChunkHasher.computeHash(Arrays.copyOf(plaintextBuffer, length)));
+ }
+
+ @Override
+ public void close() {
+ // As we don't have any resources there is nothing to close.
+ mClosed = true;
+ }
+
+ @Override
+ public byte[] getDigest() throws NoSuchAlgorithmException {
+ checkState(mClosed, "Must close() before getDigest()");
+ MessageDigest digest = getMessageDigest();
+ Collections.sort(mUnsortedHashes);
+ for (ChunkHash hash : mUnsortedHashes) {
+ digest.update(hash.getHash());
+ }
+ return digest.digest();
+ }
+
+ private static MessageDigest getMessageDigest() throws NoSuchAlgorithmException {
+ return MessageDigest.getInstance(DIGEST_ALGORITHM);
+ }
+
+ /**
+ * Returns the key value pairs from the backup, sorted lexicographically by key.
+ *
+ * <p>You must call {@link #close} first.
+ */
+ public List<KeyValuePairProto.KeyValuePair> getPairs() {
+ checkState(mClosed, "Must close() before getPairs()");
+ Collections.sort(
+ mUnsortedPairs,
+ new Comparator<KeyValuePairProto.KeyValuePair>() {
+ @Override
+ public int compare(
+ KeyValuePairProto.KeyValuePair o1, KeyValuePairProto.KeyValuePair o2) {
+ return o1.key.compareTo(o2.key);
+ }
+ });
+ return mUnsortedPairs;
+ }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/kv/KeyValueListingBuilder.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/kv/KeyValueListingBuilder.java
new file mode 100644
index 000000000000..b3518e144ce3
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/kv/KeyValueListingBuilder.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.backup.encryption.kv;
+
+import static com.android.internal.util.Preconditions.checkArgument;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import com.android.server.backup.encryption.chunk.ChunkHash;
+import com.android.server.backup.encryption.protos.nano.KeyValueListingProto;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * Builds a {@link KeyValueListingProto.KeyValueListing}, which is a nano proto and so has no
+ * builder.
+ */
+public class KeyValueListingBuilder {
+ private final List<KeyValueListingProto.KeyValueEntry> mEntries = new ArrayList<>();
+
+ /** Adds a new pair entry to the listing. */
+ public KeyValueListingBuilder addPair(String key, ChunkHash hash) {
+ checkArgument(key.length() != 0, "Key must have non-zero length");
+ checkNotNull(hash, "Hash must not be null");
+
+ KeyValueListingProto.KeyValueEntry entry = new KeyValueListingProto.KeyValueEntry();
+ entry.key = key;
+ entry.hash = hash.getHash();
+ mEntries.add(entry);
+
+ return this;
+ }
+
+ /** Adds all pairs contained in a map, where the map is from key to hash. */
+ public KeyValueListingBuilder addAll(Map<String, ChunkHash> map) {
+ for (Entry<String, ChunkHash> entry : map.entrySet()) {
+ addPair(entry.getKey(), entry.getValue());
+ }
+
+ return this;
+ }
+
+ /** Returns a new listing containing all the pairs added so far. */
+ public KeyValueListingProto.KeyValueListing build() {
+ if (mEntries.size() == 0) {
+ return emptyListing();
+ }
+
+ KeyValueListingProto.KeyValueListing listing = new KeyValueListingProto.KeyValueListing();
+ listing.entries = new KeyValueListingProto.KeyValueEntry[mEntries.size()];
+ mEntries.toArray(listing.entries);
+ return listing;
+ }
+
+ /** Returns a new listing which does not contain any pairs. */
+ public static KeyValueListingProto.KeyValueListing emptyListing() {
+ KeyValueListingProto.KeyValueListing listing = new KeyValueListingProto.KeyValueListing();
+ listing.entries = KeyValueListingProto.KeyValueEntry.emptyArray();
+ return listing;
+ }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/DecryptedChunkOutput.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/DecryptedChunkOutput.java
index e3df3c1eb96f..f67f1007f632 100644
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/DecryptedChunkOutput.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/DecryptedChunkOutput.java
@@ -19,6 +19,7 @@ package com.android.server.backup.encryption.tasks;
import java.io.Closeable;
import java.io.IOException;
import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
/**
* Accepts the plaintext bytes of decrypted chunks and writes them to some output. Also keeps track
@@ -30,7 +31,7 @@ public interface DecryptedChunkOutput extends Closeable {
*
* @return {@code this}, to allow use with try-with-resources
*/
- DecryptedChunkOutput open() throws IOException;
+ DecryptedChunkOutput open() throws IOException, NoSuchAlgorithmException;
/**
* Writes the plaintext bytes of chunk to whatever output the implementation chooses. Also
@@ -43,12 +44,13 @@ public interface DecryptedChunkOutput extends Closeable {
* at index 0.
* @param length The length in bytes of the plaintext contained in {@code plaintextBuffer}.
*/
- void processChunk(byte[] plaintextBuffer, int length) throws IOException, InvalidKeyException;
+ void processChunk(byte[] plaintextBuffer, int length)
+ throws IOException, InvalidKeyException, NoSuchAlgorithmException;
/**
* Returns the message digest of all the chunks processed by {@link #processChunk}.
*
* <p>You must call {@link Closeable#close()} before calling this method.
*/
- byte[] getDigest();
+ byte[] getDigest() throws NoSuchAlgorithmException;
}
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 f84be6d4f3f4..4e42ce7366f0 100644
--- a/packages/BackupEncryption/test/robolectric/Android.bp
+++ b/packages/BackupEncryption/test/robolectric/Android.bp
@@ -25,6 +25,11 @@ android_robolectric_test {
"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/chunk/ChunkListingMapTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunk/ChunkListingMapTest.java
index 24e5573b891d..c5f78c254cd1 100644
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunk/ChunkListingMapTest.java
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunk/ChunkListingMapTest.java
@@ -19,10 +19,8 @@ package com.android.server.backup.encryption.chunk;
import static com.google.common.truth.Truth.assertThat;
import android.platform.test.annotations.Presubmit;
-import android.util.proto.ProtoInputStream;
-import android.util.proto.ProtoOutputStream;
-import com.android.internal.util.Preconditions;
+import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
import com.google.common.base.Charsets;
@@ -31,167 +29,86 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
-import java.io.ByteArrayInputStream;
import java.util.Arrays;
@RunWith(RobolectricTestRunner.class)
@Presubmit
public class ChunkListingMapTest {
- private static final String CHUNK_A = "CHUNK_A";
- private static final String CHUNK_B = "CHUNK_B";
- private static final String CHUNK_C = "CHUNK_C";
+ private static final ChunkHash CHUNK_A_HASH = getHash("CHUNK_A");
+ private static final ChunkHash CHUNK_B_HASH = getHash("CHUNK_B");
+ private static final ChunkHash CHUNK_C_HASH = getHash("CHUNK_C");
private static final int CHUNK_A_LENGTH = 256;
private static final int CHUNK_B_LENGTH = 1024;
private static final int CHUNK_C_LENGTH = 4055;
- private ChunkHash mChunkHashA;
- private ChunkHash mChunkHashB;
- private ChunkHash mChunkHashC;
+ private static final int CHUNK_A_START = 0;
+ private static final int CHUNK_B_START = CHUNK_A_START + CHUNK_A_LENGTH;
+ private static final int CHUNK_C_START = CHUNK_B_START + CHUNK_B_LENGTH;
+
+ private ChunkListingMap mChunkListingMap;
@Before
- public void setUp() throws Exception {
- mChunkHashA = getHash(CHUNK_A);
- mChunkHashB = getHash(CHUNK_B);
- mChunkHashC = getHash(CHUNK_C);
+ public void setUp() {
+ mChunkListingMap = createFromFixture();
}
@Test
- public void testHasChunk_whenChunkInListing_returnsTrue() throws Exception {
- byte[] chunkListingProto =
- createChunkListingProto(
- new ChunkHash[] {mChunkHashA, mChunkHashB, mChunkHashC},
- new int[] {CHUNK_A_LENGTH, CHUNK_B_LENGTH, CHUNK_C_LENGTH});
- ChunkListingMap chunkListingMap =
- ChunkListingMap.readFromProto(
- new ProtoInputStream(new ByteArrayInputStream(chunkListingProto)));
-
- boolean chunkAInList = chunkListingMap.hasChunk(mChunkHashA);
- boolean chunkBInList = chunkListingMap.hasChunk(mChunkHashB);
- boolean chunkCInList = chunkListingMap.hasChunk(mChunkHashC);
-
- assertThat(chunkAInList).isTrue();
- assertThat(chunkBInList).isTrue();
- assertThat(chunkCInList).isTrue();
+ public void hasChunk_isTrueForExistingChunks() {
+ assertThat(mChunkListingMap.hasChunk(CHUNK_A_HASH)).isTrue();
+ assertThat(mChunkListingMap.hasChunk(CHUNK_B_HASH)).isTrue();
+ assertThat(mChunkListingMap.hasChunk(CHUNK_C_HASH)).isTrue();
}
@Test
- public void testHasChunk_whenChunkNotInListing_returnsFalse() throws Exception {
- byte[] chunkListingProto =
- createChunkListingProto(
- new ChunkHash[] {mChunkHashA, mChunkHashB},
- new int[] {CHUNK_A_LENGTH, CHUNK_B_LENGTH});
- ChunkListingMap chunkListingMap =
- ChunkListingMap.readFromProto(
- new ProtoInputStream(new ByteArrayInputStream(chunkListingProto)));
- ChunkHash chunkHashEmpty = getHash("");
-
- boolean chunkCInList = chunkListingMap.hasChunk(mChunkHashC);
- boolean emptyChunkInList = chunkListingMap.hasChunk(chunkHashEmpty);
-
- assertThat(chunkCInList).isFalse();
- assertThat(emptyChunkInList).isFalse();
+ public void hasChunk_isFalseForNonexistentChunks() {
+ assertThat(mChunkListingMap.hasChunk(getHash("CHUNK_D"))).isFalse();
+ assertThat(mChunkListingMap.hasChunk(getHash(""))).isFalse();
}
@Test
- public void testGetChunkEntry_returnsEntryWithCorrectLength() throws Exception {
- byte[] chunkListingProto =
- createChunkListingProto(
- new ChunkHash[] {mChunkHashA, mChunkHashB, mChunkHashC},
- new int[] {CHUNK_A_LENGTH, CHUNK_B_LENGTH, CHUNK_C_LENGTH});
- ChunkListingMap chunkListingMap =
- ChunkListingMap.readFromProto(
- new ProtoInputStream(new ByteArrayInputStream(chunkListingProto)));
-
- ChunkListingMap.Entry entryA = chunkListingMap.getChunkEntry(mChunkHashA);
- ChunkListingMap.Entry entryB = chunkListingMap.getChunkEntry(mChunkHashB);
- ChunkListingMap.Entry entryC = chunkListingMap.getChunkEntry(mChunkHashC);
-
- assertThat(entryA.getLength()).isEqualTo(CHUNK_A_LENGTH);
- assertThat(entryB.getLength()).isEqualTo(CHUNK_B_LENGTH);
- assertThat(entryC.getLength()).isEqualTo(CHUNK_C_LENGTH);
+ public void getChunkListing_hasCorrectLengths() {
+ assertThat(mChunkListingMap.getChunkEntry(CHUNK_A_HASH).getLength())
+ .isEqualTo(CHUNK_A_LENGTH);
+ assertThat(mChunkListingMap.getChunkEntry(CHUNK_B_HASH).getLength())
+ .isEqualTo(CHUNK_B_LENGTH);
+ assertThat(mChunkListingMap.getChunkEntry(CHUNK_C_HASH).getLength())
+ .isEqualTo(CHUNK_C_LENGTH);
}
@Test
- public void testGetChunkEntry_returnsEntryWithCorrectStart() throws Exception {
- byte[] chunkListingProto =
- createChunkListingProto(
- new ChunkHash[] {mChunkHashA, mChunkHashB, mChunkHashC},
- new int[] {CHUNK_A_LENGTH, CHUNK_B_LENGTH, CHUNK_C_LENGTH});
- ChunkListingMap chunkListingMap =
- ChunkListingMap.readFromProto(
- new ProtoInputStream(new ByteArrayInputStream(chunkListingProto)));
-
- ChunkListingMap.Entry entryA = chunkListingMap.getChunkEntry(mChunkHashA);
- ChunkListingMap.Entry entryB = chunkListingMap.getChunkEntry(mChunkHashB);
- ChunkListingMap.Entry entryC = chunkListingMap.getChunkEntry(mChunkHashC);
-
- assertThat(entryA.getStart()).isEqualTo(0);
- assertThat(entryB.getStart()).isEqualTo(CHUNK_A_LENGTH);
- assertThat(entryC.getStart()).isEqualTo(CHUNK_A_LENGTH + CHUNK_B_LENGTH);
+ public void getChunkListing_hasCorrectStarts() {
+ assertThat(mChunkListingMap.getChunkEntry(CHUNK_A_HASH).getStart())
+ .isEqualTo(CHUNK_A_START);
+ assertThat(mChunkListingMap.getChunkEntry(CHUNK_B_HASH).getStart())
+ .isEqualTo(CHUNK_B_START);
+ assertThat(mChunkListingMap.getChunkEntry(CHUNK_C_HASH).getStart())
+ .isEqualTo(CHUNK_C_START);
}
@Test
- public void testGetChunkEntry_returnsNullForNonExistentChunk() throws Exception {
- byte[] chunkListingProto =
- createChunkListingProto(
- new ChunkHash[] {mChunkHashA, mChunkHashB},
- new int[] {CHUNK_A_LENGTH, CHUNK_B_LENGTH});
- ChunkListingMap chunkListingMap =
- ChunkListingMap.readFromProto(
- new ProtoInputStream(new ByteArrayInputStream(chunkListingProto)));
-
- ChunkListingMap.Entry chunkEntryNonexistentChunk =
- chunkListingMap.getChunkEntry(mChunkHashC);
-
- assertThat(chunkEntryNonexistentChunk).isNull();
+ public void getChunkListing_isNullForNonExistentChunks() {
+ assertThat(mChunkListingMap.getChunkEntry(getHash("Hey"))).isNull();
}
- @Test
- public void testReadFromProto_whenEmptyProto_returnsChunkListingMapWith0Chunks()
- throws Exception {
- ProtoInputStream emptyProto = new ProtoInputStream(new ByteArrayInputStream(new byte[] {}));
-
- ChunkListingMap chunkListingMap = ChunkListingMap.readFromProto(emptyProto);
-
- assertThat(chunkListingMap.getChunkCount()).isEqualTo(0);
- }
-
- @Test
- public void testReadFromProto_returnsChunkListingWithCorrectSize() throws Exception {
- byte[] chunkListingProto =
- createChunkListingProto(
- new ChunkHash[] {mChunkHashA, mChunkHashB, mChunkHashC},
- new int[] {CHUNK_A_LENGTH, CHUNK_B_LENGTH, CHUNK_C_LENGTH});
-
- ChunkListingMap chunkListingMap =
- ChunkListingMap.readFromProto(
- new ProtoInputStream(new ByteArrayInputStream(chunkListingProto)));
-
- assertThat(chunkListingMap.getChunkCount()).isEqualTo(3);
+ private static ChunkListingMap createFromFixture() {
+ ChunksMetadataProto.ChunkListing chunkListing = new ChunksMetadataProto.ChunkListing();
+ chunkListing.chunks = new ChunksMetadataProto.Chunk[3];
+ chunkListing.chunks[0] = newChunk(CHUNK_A_HASH.getHash(), CHUNK_A_LENGTH);
+ chunkListing.chunks[1] = newChunk(CHUNK_B_HASH.getHash(), CHUNK_B_LENGTH);
+ chunkListing.chunks[2] = newChunk(CHUNK_C_HASH.getHash(), CHUNK_C_LENGTH);
+ return ChunkListingMap.fromProto(chunkListing);
}
- private byte[] createChunkListingProto(ChunkHash[] hashes, int[] lengths) {
- Preconditions.checkArgument(hashes.length == lengths.length);
- ProtoOutputStream outputStream = new ProtoOutputStream();
-
- for (int i = 0; i < hashes.length; ++i) {
- writeToProtoOutputStream(outputStream, hashes[i], lengths[i]);
- }
- outputStream.flush();
-
- return outputStream.getBytes();
- }
-
- private void writeToProtoOutputStream(ProtoOutputStream out, ChunkHash chunkHash, int length) {
- long token = out.start(ChunksMetadataProto.ChunkListing.CHUNKS);
- out.write(ChunksMetadataProto.Chunk.HASH, chunkHash.getHash());
- out.write(ChunksMetadataProto.Chunk.LENGTH, length);
- out.end(token);
- }
-
- private ChunkHash getHash(String name) {
+ private static ChunkHash getHash(String name) {
return new ChunkHash(
Arrays.copyOf(name.getBytes(Charsets.UTF_8), ChunkHash.HASH_LENGTH_BYTES));
}
+
+ public static ChunksMetadataProto.Chunk newChunk(byte[] hash, int length) {
+ ChunksMetadataProto.Chunk newChunk = new ChunksMetadataProto.Chunk();
+ newChunk.hash = Arrays.copyOf(hash, hash.length);
+ newChunk.length = length;
+ return newChunk;
+ }
}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunk/ChunkTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunk/ChunkTest.java
deleted file mode 100644
index 1796f56ce17a..000000000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunk/ChunkTest.java
+++ /dev/null
@@ -1,122 +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.server.backup.encryption.chunk;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.platform.test.annotations.Presubmit;
-import android.util.proto.ProtoInputStream;
-import android.util.proto.ProtoOutputStream;
-
-import com.google.common.base.Charsets;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-
-import java.io.ByteArrayInputStream;
-import java.util.Arrays;
-
-@RunWith(RobolectricTestRunner.class)
-@Presubmit
-public class ChunkTest {
- private static final String CHUNK_A = "CHUNK_A";
- private static final int CHUNK_A_LENGTH = 256;
-
- private ChunkHash mChunkHashA;
-
- @Before
- public void setUp() throws Exception {
- mChunkHashA = getHash(CHUNK_A);
- }
-
- @Test
- public void testReadFromProto_readsCorrectly() throws Exception {
- ProtoOutputStream out = new ProtoOutputStream();
- out.write(ChunksMetadataProto.Chunk.HASH, mChunkHashA.getHash());
- out.write(ChunksMetadataProto.Chunk.LENGTH, CHUNK_A_LENGTH);
- out.flush();
- byte[] protoBytes = out.getBytes();
-
- Chunk chunk =
- Chunk.readFromProto(new ProtoInputStream(new ByteArrayInputStream(protoBytes)));
-
- assertThat(chunk.getHash()).isEqualTo(mChunkHashA.getHash());
- assertThat(chunk.getLength()).isEqualTo(CHUNK_A_LENGTH);
- }
-
- @Test
- public void testReadFromProto_whenFieldsWrittenInReversedOrder_readsCorrectly()
- throws Exception {
- ProtoOutputStream out = new ProtoOutputStream();
- // Write fields of Chunk proto in reverse order.
- out.write(ChunksMetadataProto.Chunk.LENGTH, CHUNK_A_LENGTH);
- out.write(ChunksMetadataProto.Chunk.HASH, mChunkHashA.getHash());
- out.flush();
- byte[] protoBytes = out.getBytes();
-
- Chunk chunk =
- Chunk.readFromProto(new ProtoInputStream(new ByteArrayInputStream(protoBytes)));
-
- assertThat(chunk.getHash()).isEqualTo(mChunkHashA.getHash());
- assertThat(chunk.getLength()).isEqualTo(CHUNK_A_LENGTH);
- }
-
- @Test
- public void testReadFromProto_whenEmptyProto_returnsEmptyHash() throws Exception {
- ProtoInputStream emptyProto = new ProtoInputStream(new ByteArrayInputStream(new byte[] {}));
-
- Chunk chunk = Chunk.readFromProto(emptyProto);
-
- assertThat(chunk.getHash()).asList().hasSize(0);
- assertThat(chunk.getLength()).isEqualTo(0);
- }
-
- @Test
- public void testReadFromProto_whenOnlyHashSet_returnsChunkWithOnlyHash() throws Exception {
- ProtoOutputStream out = new ProtoOutputStream();
- out.write(ChunksMetadataProto.Chunk.HASH, mChunkHashA.getHash());
- out.flush();
- byte[] protoBytes = out.getBytes();
-
- Chunk chunk =
- Chunk.readFromProto(new ProtoInputStream(new ByteArrayInputStream(protoBytes)));
-
- assertThat(chunk.getHash()).isEqualTo(mChunkHashA.getHash());
- assertThat(chunk.getLength()).isEqualTo(0);
- }
-
- @Test
- public void testReadFromProto_whenOnlyLengthSet_returnsChunkWithOnlyLength() throws Exception {
- ProtoOutputStream out = new ProtoOutputStream();
- out.write(ChunksMetadataProto.Chunk.LENGTH, CHUNK_A_LENGTH);
- out.flush();
- byte[] protoBytes = out.getBytes();
-
- Chunk chunk =
- Chunk.readFromProto(new ProtoInputStream(new ByteArrayInputStream(protoBytes)));
-
- assertThat(chunk.getHash()).isEqualTo(new byte[] {});
- assertThat(chunk.getLength()).isEqualTo(CHUNK_A_LENGTH);
- }
-
- private ChunkHash getHash(String name) {
- return new ChunkHash(
- Arrays.copyOf(name.getBytes(Charsets.UTF_8), ChunkHash.HASH_LENGTH_BYTES));
- }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java
index 634acdc42eaf..7e1fdedcac80 100644
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java
@@ -24,7 +24,7 @@ import static org.mockito.Mockito.mock;
import android.platform.test.annotations.Presubmit;
import com.android.server.backup.encryption.chunk.ChunkHash;
-import com.android.server.backup.encryption.chunk.ChunksMetadataProto;
+import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
import org.junit.Before;
import org.junit.Test;
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java
index d231603e18b1..6f58ee148b83 100644
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java
@@ -24,7 +24,7 @@ import static org.mockito.Mockito.mock;
import android.platform.test.annotations.Presubmit;
import com.android.server.backup.encryption.chunk.ChunkHash;
-import com.android.server.backup.encryption.chunk.ChunksMetadataProto;
+import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
import org.junit.Before;
import org.junit.Test;
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/kv/DecryptedChunkKvOutputTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/kv/DecryptedChunkKvOutputTest.java
new file mode 100644
index 000000000000..215e1cbc725e
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/kv/DecryptedChunkKvOutputTest.java
@@ -0,0 +1,164 @@
+/*
+ * 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.kv;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertThrows;
+
+import android.os.Debug;
+import android.platform.test.annotations.Presubmit;
+
+import com.android.server.backup.encryption.chunk.ChunkHash;
+import com.android.server.backup.encryption.chunking.ChunkHasher;
+import com.android.server.backup.encryption.protos.nano.KeyValuePairProto;
+
+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.MessageDigest;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Stream;
+
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class DecryptedChunkKvOutputTest {
+ private static final String TEST_KEY_1 = "key_1";
+ private static final String TEST_KEY_2 = "key_2";
+ private static final byte[] TEST_VALUE_1 = {1, 2, 3};
+ private static final byte[] TEST_VALUE_2 = {10, 11, 12, 13};
+ private static final byte[] TEST_PAIR_1 = toByteArray(createPair(TEST_KEY_1, TEST_VALUE_1));
+ private static final byte[] TEST_PAIR_2 = toByteArray(createPair(TEST_KEY_2, TEST_VALUE_2));
+ private static final int TEST_BUFFER_SIZE = Math.max(TEST_PAIR_1.length, TEST_PAIR_2.length);
+
+ @Mock private ChunkHasher mChunkHasher;
+ private DecryptedChunkKvOutput mOutput;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ when(mChunkHasher.computeHash(any()))
+ .thenAnswer(invocation -> fakeHash(invocation.getArgument(0)));
+ mOutput = new DecryptedChunkKvOutput(mChunkHasher);
+ }
+
+ @Test
+ public void open_returnsInstance() throws Exception {
+ assertThat(mOutput.open()).isEqualTo(mOutput);
+ }
+
+ @Test
+ public void processChunk_alreadyClosed_throws() throws Exception {
+ mOutput.open();
+ mOutput.close();
+
+ assertThrows(
+ IllegalStateException.class,
+ () -> mOutput.processChunk(TEST_PAIR_1, TEST_PAIR_1.length));
+ }
+
+ @Test
+ public void getDigest_beforeClose_throws() throws Exception {
+ // TODO: b/141356823 We should add a test which calls .open() here
+ assertThrows(IllegalStateException.class, () -> mOutput.getDigest());
+ }
+
+ @Test
+ public void getDigest_returnsDigestOfSortedHashes() throws Exception {
+ mOutput.open();
+ Debug.waitForDebugger();
+ mOutput.processChunk(Arrays.copyOf(TEST_PAIR_1, TEST_BUFFER_SIZE), TEST_PAIR_1.length);
+ mOutput.processChunk(Arrays.copyOf(TEST_PAIR_2, TEST_BUFFER_SIZE), TEST_PAIR_2.length);
+ mOutput.close();
+
+ byte[] actualDigest = mOutput.getDigest();
+
+ MessageDigest digest = MessageDigest.getInstance(DecryptedChunkKvOutput.DIGEST_ALGORITHM);
+ Stream.of(TEST_PAIR_1, TEST_PAIR_2)
+ .map(DecryptedChunkKvOutputTest::fakeHash)
+ .sorted(Comparator.naturalOrder())
+ .forEachOrdered(hash -> digest.update(hash.getHash()));
+ assertThat(actualDigest).isEqualTo(digest.digest());
+ }
+
+ @Test
+ public void getPairs_beforeClose_throws() throws Exception {
+ // TODO: b/141356823 We should add a test which calls .open() here
+ assertThrows(IllegalStateException.class, () -> mOutput.getPairs());
+ }
+
+ @Test
+ public void getPairs_returnsPairsSortedByKey() throws Exception {
+ mOutput.open();
+ // Write out of order to check that it sorts the chunks.
+ mOutput.processChunk(Arrays.copyOf(TEST_PAIR_2, TEST_BUFFER_SIZE), TEST_PAIR_2.length);
+ mOutput.processChunk(Arrays.copyOf(TEST_PAIR_1, TEST_BUFFER_SIZE), TEST_PAIR_1.length);
+ mOutput.close();
+
+ List<KeyValuePairProto.KeyValuePair> pairs = mOutput.getPairs();
+
+ assertThat(
+ isInOrder(
+ pairs,
+ Comparator.comparing(
+ (KeyValuePairProto.KeyValuePair pair) -> pair.key)))
+ .isTrue();
+ assertThat(pairs).hasSize(2);
+ assertThat(pairs.get(0).key).isEqualTo(TEST_KEY_1);
+ assertThat(pairs.get(0).value).isEqualTo(TEST_VALUE_1);
+ assertThat(pairs.get(1).key).isEqualTo(TEST_KEY_2);
+ assertThat(pairs.get(1).value).isEqualTo(TEST_VALUE_2);
+ }
+
+ private static KeyValuePairProto.KeyValuePair createPair(String key, byte[] value) {
+ KeyValuePairProto.KeyValuePair pair = new KeyValuePairProto.KeyValuePair();
+ pair.key = key;
+ pair.value = value;
+ return pair;
+ }
+
+ private boolean isInOrder(
+ List<KeyValuePairProto.KeyValuePair> list,
+ Comparator<KeyValuePairProto.KeyValuePair> comparator) {
+ if (list.size() < 2) {
+ return true;
+ }
+
+ List<KeyValuePairProto.KeyValuePair> sortedList = new ArrayList<>(list);
+ Collections.sort(sortedList, comparator);
+ return list.equals(sortedList);
+ }
+
+ private static byte[] toByteArray(KeyValuePairProto.KeyValuePair nano) {
+ return KeyValuePairProto.KeyValuePair.toByteArray(nano);
+ }
+
+ private static ChunkHash fakeHash(byte[] data) {
+ return new ChunkHash(Arrays.copyOf(data, ChunkHash.HASH_LENGTH_BYTES));
+ }
+}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/kv/KeyValueListingBuilderTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/kv/KeyValueListingBuilderTest.java
new file mode 100644
index 000000000000..acc662860528
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/kv/KeyValueListingBuilderTest.java
@@ -0,0 +1,110 @@
+/*
+ * 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.kv;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.platform.test.annotations.Presubmit;
+
+import com.android.server.backup.encryption.chunk.ChunkHash;
+import com.android.server.backup.encryption.protos.nano.KeyValueListingProto;
+
+import com.google.common.collect.ImmutableMap;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import java.util.Arrays;
+
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class KeyValueListingBuilderTest {
+ private static final String TEST_KEY_1 = "test_key_1";
+ private static final String TEST_KEY_2 = "test_key_2";
+ private static final ChunkHash TEST_HASH_1 =
+ new ChunkHash(Arrays.copyOf(new byte[] {1, 2}, ChunkHash.HASH_LENGTH_BYTES));
+ private static final ChunkHash TEST_HASH_2 =
+ new ChunkHash(Arrays.copyOf(new byte[] {5, 6}, ChunkHash.HASH_LENGTH_BYTES));
+
+ private KeyValueListingBuilder mBuilder;
+
+ @Before
+ public void setUp() {
+ mBuilder = new KeyValueListingBuilder();
+ }
+
+ @Test
+ public void addPair_nullKey_throws() {
+ assertThrows(NullPointerException.class, () -> mBuilder.addPair(null, TEST_HASH_1));
+ }
+
+ @Test
+ public void addPair_emptyKey_throws() {
+ assertThrows(IllegalArgumentException.class, () -> mBuilder.addPair("", TEST_HASH_1));
+ }
+
+ @Test
+ public void addPair_nullHash_throws() {
+ assertThrows(NullPointerException.class, () -> mBuilder.addPair(TEST_KEY_1, null));
+ }
+
+ @Test
+ public void build_noPairs_buildsEmptyListing() {
+ KeyValueListingProto.KeyValueListing listing = mBuilder.build();
+
+ assertThat(listing.entries).isEmpty();
+ }
+
+ @Test
+ public void build_returnsCorrectListing() {
+ mBuilder.addPair(TEST_KEY_1, TEST_HASH_1);
+
+ KeyValueListingProto.KeyValueListing listing = mBuilder.build();
+
+ assertThat(listing.entries.length).isEqualTo(1);
+ assertThat(listing.entries[0].key).isEqualTo(TEST_KEY_1);
+ assertThat(listing.entries[0].hash).isEqualTo(TEST_HASH_1.getHash());
+ }
+
+ @Test
+ public void addAll_addsAllPairsInMap() {
+ ImmutableMap<String, ChunkHash> pairs =
+ new ImmutableMap.Builder<String, ChunkHash>()
+ .put(TEST_KEY_1, TEST_HASH_1)
+ .put(TEST_KEY_2, TEST_HASH_2)
+ .build();
+
+ mBuilder.addAll(pairs);
+ KeyValueListingProto.KeyValueListing listing = mBuilder.build();
+
+ assertThat(listing.entries.length).isEqualTo(2);
+ assertThat(listing.entries[0].key).isEqualTo(TEST_KEY_1);
+ assertThat(listing.entries[0].hash).isEqualTo(TEST_HASH_1.getHash());
+ assertThat(listing.entries[1].key).isEqualTo(TEST_KEY_2);
+ assertThat(listing.entries[1].hash).isEqualTo(TEST_HASH_2.getHash());
+ }
+
+ @Test
+ public void emptyListing_returnsListingWithoutAnyPairs() {
+ KeyValueListingProto.KeyValueListing emptyListing = KeyValueListingBuilder.emptyListing();
+ assertThat(emptyListing.entries).isEmpty();
+ }
+}
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/BackupRestoreConfirmation/res/values-in/strings.xml b/packages/BackupRestoreConfirmation/res/values-in/strings.xml
index 63a3772d863d..5c82adff83fa 100644
--- a/packages/BackupRestoreConfirmation/res/values-in/strings.xml
+++ b/packages/BackupRestoreConfirmation/res/values-in/strings.xml
@@ -16,9 +16,9 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="backup_confirm_title" msgid="827563724209303345">"Backup sepenuhnya"</string>
+ <string name="backup_confirm_title" msgid="827563724209303345">"Pencadangan lengkap"</string>
<string name="restore_confirm_title" msgid="5469365809567486602">"Pemulihan sepenuhnya"</string>
- <string name="backup_confirm_text" msgid="1878021282758896593">"Backup lengkap semua data ke komputer yang tersambung telah diminta. Apakah Anda ingin mengizinkan hal ini dilakukan?\n\nJika Anda tidak meminta backup ini, jangan izinkan operasi dilanjutkan."</string>
+ <string name="backup_confirm_text" msgid="1878021282758896593">"Pencadangan semua data ke komputer yang tersambung telah diminta. Apakah Anda ingin mengizinkan hal ini dilakukan?\n\nJika Anda tidak meminta pencadangan ini, jangan izinkan operasi dilanjutkan."</string>
<string name="allow_backup_button_label" msgid="4217228747769644068">"Cadangkan data saya"</string>
<string name="deny_backup_button_label" msgid="6009119115581097708">"Jangan mencadangkan"</string>
<string name="restore_confirm_text" msgid="7499866728030461776">"Pemulihan lengkap semua data dari komputer desktop yang tersambung telah diminta. Apakah Anda ingin mengizinkan hal ini?\n\nJika Anda tidak meminta pemulihan ini, jangan izinkan operasi dilanjutkan. Operasi ini akan mengganti data apa pun yang saat ini ada dalam perangkat!"</string>
@@ -31,8 +31,8 @@
<string name="backup_enc_password_optional" msgid="1350137345907579306">"Jika Anda ingin mengenkripsi data cadangan lengkap, masukkan sandi di bawah:"</string>
<string name="backup_enc_password_required" msgid="7889652203371654149">"Karena perangkat Anda dienkripsi, Anda perlu mengenkripsi cadangan. Masukkan sandi di bawah:"</string>
<string name="restore_enc_password_text" msgid="6140898525580710823">"Jika data pemulihan dienkripsi, masukkan sandi di bawah:"</string>
- <string name="toast_backup_started" msgid="550354281452756121">"Backup dimulai..."</string>
- <string name="toast_backup_ended" msgid="3818080769548726424">"Backup selesai"</string>
+ <string name="toast_backup_started" msgid="550354281452756121">"Pencadangan dimulai..."</string>
+ <string name="toast_backup_ended" msgid="3818080769548726424">"Pencadangan selesai"</string>
<string name="toast_restore_started" msgid="7881679218971277385">"Pemulihan dimulai..."</string>
<string name="toast_restore_ended" msgid="1764041639199696132">"Pemulihan berakhir"</string>
<string name="toast_timeout" msgid="5276598587087626877">"Waktu tunggu operasi habis"</string>
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/src/com/android/systemui/CarSystemUIRootComponent.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java
index 27146fbac789..264b7d52c02f 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java
@@ -25,8 +25,6 @@ import dagger.Component;
modules = {
DependencyProvider.class,
DependencyBinder.class,
- ServiceBinder.class,
- SystemUIBinder.class,
SystemUIFactory.ContextHolder.class,
SystemUIModule.class,
CarSystemUIModule.class
diff --git a/packages/CarrierDefaultApp/tests/unit/Android.bp b/packages/CarrierDefaultApp/tests/unit/Android.bp
index 96144cf76ff7..5655abb046b2 100644
--- a/packages/CarrierDefaultApp/tests/unit/Android.bp
+++ b/packages/CarrierDefaultApp/tests/unit/Android.bp
@@ -17,7 +17,6 @@ android_test {
certificate: "platform",
libs: [
"android.test.runner",
- "telephony-common",
"android.test.base",
],
static_libs: [
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/SettingsLib/SearchWidget/res/values-km/strings.xml b/packages/SettingsLib/SearchWidget/res/values-km/strings.xml
index f012e3ab3e83..7ac9cb1b72ce 100644
--- a/packages/SettingsLib/SearchWidget/res/values-km/strings.xml
+++ b/packages/SettingsLib/SearchWidget/res/values-km/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">"ការកំណត់ការ​ស្វែងរក"</string>
+ <string name="search_menu" msgid="1604061903696928905">"​ស្វែងរកការកំណត់"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index afdb105ca560..264954465981 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -385,10 +385,10 @@
<string name="power_discharging_duration_enhanced" msgid="1992003260664804080">"Temps restant aproximat segons l\'ús que en fas: <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">"La bateria hauria de durar aproximadament fins a les <xliff:g id="TIME">%1$s</xliff:g> segons l\'ús que en fas (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
- <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"La bateria hauria de durar aproximadament fins a les <xliff:g id="TIME">%1$s</xliff:g> segons l\'ús que en fas"</string>
- <string name="power_discharge_by" msgid="6453537733650125582">"La bateria hauria de durar aproximadament fins a les <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">"La bateria hauria de durar aproximadament fins a les <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_enhanced" msgid="2095821536747992464">"Hauria de durar aproximadament fins a les <xliff:g id="TIME">%1$s</xliff:g> segons l\'ús que en facis (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Hauria de durar aproximadament fins a les <xliff:g id="TIME">%1$s</xliff:g> segons l\'ús que en facis"</string>
+ <string name="power_discharge_by" msgid="6453537733650125582">"Hauria de durar aproximadament fins a les <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">"Hauria de durar aproximadament fins a les <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="1372817269546888804">"Fins a les <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_suggestion_extend_battery" msgid="4401408879069551485">"Allarga la durada de la bateria després de les <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Temps restant inferior a <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index a74b4ae3ae9e..16d7ea4352cf 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -169,7 +169,7 @@
<string name="tts_engine_security_warning" msgid="8786238102020223650">"Այս խոսքային սինթեզի գործիքը կարող է հավաքել այն ամենը, ինչ արտասանված է, այդ թվում` անձնական տվյալներ, ինչպիսիք են գաղտնաբառն ու բանկային քարտի համարները: Սկզբնաբյուրը <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> շարժիչն է: Միացնե՞լ խոսքի սինթեզի շարժիչի օգտագործումը:"</string>
<string name="tts_engine_network_required" msgid="1190837151485314743">"Այս լեզուն պահանջում է աշխատող ցանցային կապ գրվածքից խոսք ելքի համար:"</string>
<string name="tts_default_sample_string" msgid="4040835213373086322">"Սա խոսքային սինթեզի մի նմուշ է:"</string>
- <string name="tts_status_title" msgid="7268566550242584413">"Լռելյայն լեզվի կարգավիճակը"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Կանխադրված լեզվի կարգավիճակը"</string>
<string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g>-ը լիովին աջակցվում է"</string>
<string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g>-ը պահանջում է ցանցային կապ"</string>
<string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g>-ը չի աջակցվում"</string>
@@ -201,9 +201,9 @@
<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="enable_adb" msgid="7982306934419797485">"USB վրիպազերծում"</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>
+ <string name="clear_adb_keys" msgid="4038889221503122743">"Չեղարկել USB-ով վրիպազերծման թույլտվությունները"</string>
<string name="bugreport_in_power" msgid="7923901846375587241">"Սխալի հաղորդման դյուրանցում"</string>
<string name="bugreport_in_power_summary" msgid="1778455732762984579">"Գործարկման ցանկում ցույց տալ կոճակը՝ վրիպակների հաղորդման համար"</string>
<string name="keep_screen_on" msgid="1146389631208760344">"Մնալ արթուն"</string>
@@ -263,9 +263,9 @@
<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_message" msgid="7316799925425402244">"USB վրիպազերծումը միայն ծրագրավորման նպատակների համար է: Օգտագործեք այն ձեր համակարգչից տվյալները ձեր սարք պատճենելու համար, առանց ծանուցման ձեր սարքի վրա ծրագրեր տեղադրելու և տվյալների մատյանը ընթերցելու համար:"</string>
- <string name="adb_keys_warning_message" msgid="5659849457135841625">"Փակե՞լ 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>
<string name="dev_settings_warning_message" msgid="2298337781139097964">"Այս կարգավորումները միայն ծրագրավորման նպատակների համար են նախատեսված: Դրանք կարող են խանգարել ձեր սարքի կամ ծրագրի աշխատանքին:"</string>
<string name="verify_apps_over_usb_title" msgid="4177086489869041953">"Ստուգել հավելվածները USB-ի նկատմամբ"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 7fe1ef9213ed..02c4c8c6f44d 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -336,8 +336,8 @@
<string name="force_resizable_activities_summary" msgid="6667493494706124459">"Buat semua aktivitas dapat diubah ukurannya untuk banyak jendela, terlepas dari nilai manifes."</string>
<string name="enable_freeform_support" msgid="1461893351278940416">"Aktifkan jendela berformat bebas"</string>
<string name="enable_freeform_support_summary" msgid="8247310463288834487">"Aktifkan dukungan untuk jendela eksperimental berformat bebas."</string>
- <string name="local_backup_password_title" msgid="3860471654439418822">"Sandi backup desktop"</string>
- <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Saat ini backup desktop sepenuhnya tidak dilindungi"</string>
+ <string name="local_backup_password_title" msgid="3860471654439418822">"Sandi cadangan desktop"</string>
+ <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Saat ini cadangan desktop penuh tidak dilindungi"</string>
<string name="local_backup_password_summary_change" msgid="5376206246809190364">"Ketuk guna mengubah atau menghapus sandi untuk cadangan lengkap desktop"</string>
<string name="local_backup_password_toast_success" msgid="582016086228434290">"Sandi cadangan baru telah disetel"</string>
<string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Sandi baru dan konfirmasinya tidak cocok."</string>
@@ -387,8 +387,8 @@
<skip />
<string name="power_discharge_by_enhanced" msgid="2095821536747992464">"Akan bertahan kira-kira sampai <xliff:g id="TIME">%1$s</xliff:g> berdasarkan penggunaan Anda (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Akan bertahan kira-kira sampai <xliff:g id="TIME">%1$s</xliff:g> berdasarkan penggunaan Anda"</string>
- <string name="power_discharge_by" msgid="6453537733650125582">"Akan bertahan kira-kira sampai <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">"Akan bertahan kira-kira sampai <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by" msgid="6453537733650125582">"Akan bertahan kira-kira sampai pukul <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">"Akan bertahan kira-kira sampai pukul <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="1372817269546888804">"Hingga <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_suggestion_extend_battery" msgid="4401408879069551485">"Perpanjang masa pakai baterai hingga <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Tersisa kurang dari <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 68c0f17a3b41..a43e8d6aa4e4 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -240,7 +240,7 @@
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Streaming: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
<string name="select_private_dns_configuration_title" msgid="3700456559305263922">"DNS privato"</string>
<string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Seleziona modalità DNS privato"</string>
- <string name="private_dns_mode_off" msgid="8236575187318721684">"Non attivo"</string>
+ <string name="private_dns_mode_off" msgid="8236575187318721684">"Off"</string>
<string name="private_dns_mode_opportunistic" msgid="8314986739896927399">"Automatico"</string>
<string name="private_dns_mode_provider" msgid="8354935160639360804">"Nome host del provider DNS privato"</string>
<string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Inserisci il nome host del provider DNS"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index ead0bfb3fe7b..a3e049c8b144 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -342,7 +342,7 @@
<string name="local_backup_password_toast_success" msgid="582016086228434290">"הוגדרה סיסמת גיבוי חדשה"</string>
<string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"הסיסמה החדשה והאישור אינם תואמים"</string>
<string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"הגדרת סיסמת גיבוי נכשלה"</string>
- <string name="loading_injected_setting_summary" msgid="4095178591461231376">"טוען…"</string>
+ <string name="loading_injected_setting_summary" msgid="4095178591461231376">"בטעינה…"</string>
<string-array name="color_mode_names">
<item msgid="2425514299220523812">"דינמי (ברירת מחדל)"</item>
<item msgid="8446070607501413455">"טבעי"</item>
@@ -405,7 +405,7 @@
<string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> עד לטעינה מלאה"</string>
<string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> עד לטעינה מלאה"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"לא ידוע"</string>
- <string name="battery_info_status_charging" msgid="1705179948350365604">"טוען"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"בטעינה"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"בטעינה"</string>
<string name="battery_info_status_discharging" msgid="310932812698268588">"לא בטעינה"</string>
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"המכשיר מחובר, אבל לא ניתן לטעון עכשיו"</string>
diff --git a/packages/SettingsLib/res/values-ko/arrays.xml b/packages/SettingsLib/res/values-ko/arrays.xml
index e2d8e0245d93..15e11dbe5afe 100644
--- a/packages/SettingsLib/res/values-ko/arrays.xml
+++ b/packages/SettingsLib/res/values-ko/arrays.xml
@@ -226,7 +226,7 @@
</string-array>
<string-array name="enable_opengl_traces_entries">
<item msgid="3191973083884253830">"없음"</item>
- <item msgid="9089630089455370183">"로그캣"</item>
+ <item msgid="9089630089455370183">"Logcat"</item>
<item msgid="5397807424362304288">"Systrace(그래픽)"</item>
<item msgid="1340692776955662664">"glGetError의 스택 호출"</item>
</string-array>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index ed88bc53890b..1cee8fbe5a04 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/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">"Should last until about <xliff:g id="TIME">%1$s</xliff:g> based on your usage (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
- <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Should last until about <xliff:g id="TIME">%1$s</xliff:g> based on your usage"</string>
- <string name="power_discharge_by" msgid="6453537733650125582">"Should last until about <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">"Should last until about <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-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index 8feb5ab43ee4..96aebc59c556 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -403,7 +403,7 @@
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Iespējams, ierīce drīz izslēgsies (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> — <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Atlikušais laiks līdz pilnai uzlādei: <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>, kamēr pilnībā uzlādēts"</string>
+ <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> līdz pilnīgai uzlādei"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Nezināms"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Uzlāde"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"notiek uzlāde"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index dda346242616..d93304fc3bf6 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -153,7 +153,7 @@
<string name="launch_defaults_some" msgid="313159469856372621">"Поставени се некои стандардни вредности"</string>
<string name="launch_defaults_none" msgid="4241129108140034876">"Нема поставено стандардни вредности"</string>
<string name="tts_settings" msgid="8186971894801348327">"Поставки на текст-во-говор"</string>
- <string name="tts_settings_title" msgid="1237820681016639683">"Излез текст-во-говор"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Претворање текст во говор"</string>
<string name="tts_default_rate_title" msgid="6030550998379310088">"Брзина на говор"</string>
<string name="tts_default_rate_summary" msgid="4061815292287182801">"Брзина со која се кажува текстот"</string>
<string name="tts_default_pitch_title" msgid="6135942113172488671">"Интензитет"</string>
@@ -167,7 +167,7 @@
<string name="tts_install_data_title" msgid="4264378440508149986">"Инсталирај гласовни податоци"</string>
<string name="tts_install_data_summary" msgid="5742135732511822589">"Инсталирај ги гласовните податоци потребни за синтеза на говор"</string>
<string name="tts_engine_security_warning" msgid="8786238102020223650">"Овој софтвер за синтеза на говор може да го собере сиот текст што ќе се говори, вклучувајќи и лични податоци како што се лозинки и броеви на кредитни картички. Тоа го прави апликацијата <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>. Употреби го овој софтвер за синтеза на говор?"</string>
- <string name="tts_engine_network_required" msgid="1190837151485314743">"За овој јазик е потребно поврување со мрежа што функционира на излез за текст-во-говор."</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"За претворање текст во говор на овој јазик, потребна е активна мрежна врска."</string>
<string name="tts_default_sample_string" msgid="4040835213373086322">"Ова е пример на синтеза на говор"</string>
<string name="tts_status_title" msgid="7268566550242584413">"Статус на стандарден јазик"</string>
<string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> е целосно поддржан"</string>
@@ -375,7 +375,7 @@
<string name="daltonizer_mode_deuteranomaly" msgid="5475532989673586329">"Девтераномалија (слепило за црвена и зелена)"</string>
<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_title" msgid="5800761362678707872">"Корекција на бои"</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>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index f1934c37369e..b2e1cd62b963 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -153,7 +153,7 @@
<string name="launch_defaults_some" msgid="313159469856372621">"केही पूर्वनिर्धारितहरू सेट गरिएका छन्"</string>
<string name="launch_defaults_none" msgid="4241129108140034876">"कुनै पूर्वनिर्धारित सेट गरिएको छैन"</string>
<string name="tts_settings" msgid="8186971894801348327">"पाठ-वाचन सेटिङहरू"</string>
- <string name="tts_settings_title" msgid="1237820681016639683">"पाठवाचकको उत्पादन"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"पाठवाचकको आउटपुट"</string>
<string name="tts_default_rate_title" msgid="6030550998379310088">"वाणी दर"</string>
<string name="tts_default_rate_summary" msgid="4061815292287182801">"पाठ वाचन हुने गति"</string>
<string name="tts_default_pitch_title" msgid="6135942113172488671">"पिच"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 5725c22a5456..f9e0d39f60c1 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -402,7 +402,7 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"ଖୁବ୍ ଶୀଘ୍ର ଟାବଲେଟ୍‌ଟି ବନ୍ଦ ହୋଇଯାଇପାରେ (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"ଖୁବ୍ ଶୀଘ୍ର ଡିଭାଇସ୍‌ଟି ବନ୍ଦ ହୋଇଯାଇପାରେ(<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"ସମ୍ପୂର୍ଣ୍ଣ ଚାର୍ଜ ହେବାପାଇଁ <xliff:g id="TIME">%1$s</xliff:g> ଅବଶିଷ୍ଟ ଅଛି"</string>
+ <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"ସମ୍ପୂର୍ଣ୍ଣ ଚାର୍ଜ ହେବାପାଇଁ <xliff:g id="TIME">%1$s</xliff:g> ବାକି ଅଛି"</string>
<string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ପୂର୍ଣ୍ଣ ଚାର୍ଜ ହେବା ପର୍ଯ୍ୟନ୍ତ"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"ଅଜ୍ଞାତ"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"ଚାର୍ଜ ହେଉଛି"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index faccda7e9395..b5acd8e15acd 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -388,7 +388,7 @@
<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" 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-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 501cf5049f09..c076d328155b 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -387,8 +387,8 @@
<skip />
<string name="power_discharge_by_enhanced" msgid="2095821536747992464">"Deve durar até cerca da(s) <xliff:g id="TIME">%1$s</xliff:g> com base na sua utilização (<xliff:g id="LEVEL">%2$s</xliff:g>)."</string>
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Deve durar até cerca da(s) <xliff:g id="TIME">%1$s</xliff:g> com base na sua utilização."</string>
- <string name="power_discharge_by" msgid="6453537733650125582">"Deve durar até cerca da(s) <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">"Deve durar até cerca da(s) <xliff:g id="TIME">%1$s</xliff:g>."</string>
+ <string name="power_discharge_by" msgid="6453537733650125582">"Deve durar até cerca da(s) <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">"Deve durar até cerca da(s) <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="1372817269546888804">"Até à(s) <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_suggestion_extend_battery" msgid="4401408879069551485">"Prolongar a autonomia da bateria após <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Resta(m) menos de <xliff:g id="THRESHOLD">%1$s</xliff:g>."</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index e28825c052e7..d77e1eb2a713 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -385,10 +385,10 @@
<string name="power_discharging_duration_enhanced" msgid="1992003260664804080">"Zostáva približne <xliff:g id="TIME_REMAINING">%1$s</xliff:g> – závisí to od intenzity využitia (<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">"Mal by vydržať približne do <xliff:g id="TIME">%1$s</xliff:g> v závislosti od využitia (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
- <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Mal by vydržať približne do <xliff:g id="TIME">%1$s</xliff:g> v závislosti od využitia"</string>
- <string name="power_discharge_by" msgid="6453537733650125582">"Mal by vydržať približne <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">"Mal by vydržať približne do <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_enhanced" msgid="2095821536747992464">"Podľa vášho využitia (<xliff:g id="LEVEL">%2$s</xliff:g>) vydrží asi do <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Podľa vášho využitia vydrží asi do <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by" msgid="6453537733650125582">"Vydrží asi do <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">"Vydrží asi do <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="1372817269546888804">"Do <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_suggestion_extend_battery" msgid="4401408879069551485">"Predĺžiť výdrž batérie minimálne do <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Zostáva menej ako <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Tablet sa môže čoskoro vypnúť (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Zariadenie sa môže čoskoro vypnúť (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Zostávajúci čas do úplného nabitia: <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabitia"</string>
+ <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> do úplného nabitia"</string>
+ <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g> do úplného nabitia"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Neznáme"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Nabíja sa"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"nabíja sa"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index b16d5d91f7eb..263b4d0e2eef 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -388,7 +388,7 @@
<string name="power_discharge_by_enhanced" msgid="2095821536747992464">"Naprava bi morala glede na način uporabe (<xliff:g id="LEVEL">%2$s</xliff:g>) delovati do približno <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Naprava bi morala glede na način uporabe delovati do približno <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Naprava bi morala delovati do približno <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">"Naprava bi morala delovati do približno <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only" msgid="107616694963545745">"Moralo bi zadostovati do približno <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="1372817269546888804">"Do <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_suggestion_extend_battery" msgid="4401408879069551485">"Podaljšanje časa delovanja akumulatorja za dlje kot <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Preostalo manj kot <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 0073003bdcca..8f72ab6f2a6c 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -402,7 +402,7 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"டேப்லெட் விரைவில் ஆஃப் ஆகக்கூடும் (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"சாதனம் விரைவில் ஆஃப் ஆகக்கூடும் (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"முழு சார்ஜாக <xliff:g id="TIME">%1$s</xliff:g> ஆகும்"</string>
+ <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"முழு சார்ஜாக <xliff:g id="TIME">%1$s</xliff:g>ம் ஆகும்"</string>
<string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - முழு சார்ஜாக <xliff:g id="TIME">%2$s</xliff:g> ஆகும்"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"அறியப்படாத"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"சார்ஜ் ஆகிறது"</string>
@@ -437,7 +437,7 @@
<string name="use_system_language_to_select_input_method_subtypes" msgid="5747329075020379587">"சாதன மொழிகளைப் பயன்படுத்து"</string>
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> க்கான அமைப்புகளைத் திறப்பதில் தோல்வி"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"இந்த உள்ளீட்டு முறையானது, கடவுச்சொற்கள் மற்றும் கிரெடிட் கார்டு எண்கள் போன்ற தனிப்பட்ட தகவல் உள்பட நீங்கள் உள்ளிடும் எல்லா உரையையும் சேகரிக்கக்கூடும். இது <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> பயன்பாட்டிலிருந்து வந்துள்ளது. இந்த உள்ளீட்டு முறையைப் பயன்படுத்தவா?"</string>
- <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"குறிப்பு: மறுதொடக்கம் செய்த பிறகு, மொபைலைத் திறக்கும் வரை இந்தப் பயன்பாட்டால் தொடங்க முடியாது"</string>
+ <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"குறிப்பு: மறுதொடக்கம் செய்த பிறகு, மொபைலைத் திறக்கும் வரை இந்த ஆப்ஸால் தொடங்க முடியாது"</string>
<string name="ims_reg_title" msgid="7609782759207241443">"IMS பதிவின் நிலை"</string>
<string name="ims_reg_status_registered" msgid="933003316932739188">"பதிவு செய்யப்பட்டது"</string>
<string name="ims_reg_status_not_registered" msgid="6529783773485229486">"பதிவு செய்யப்படவில்லை"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 0b184d241b55..b3bef9de2dcc 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -403,7 +403,7 @@
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"อุปกรณ์อาจปิดเครื่องในไม่ช้า (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"อีก <xliff:g id="TIME">%1$s</xliff:g> จึงจะชาร์จเต็ม"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> จนกว่าจะชาร์จเต็ม"</string>
+ <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>จนกว่าจะชาร์จเต็ม"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"ไม่ทราบ"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"กำลังชาร์จ"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"กำลังชาร์จ"</string>
diff --git a/packages/SettingsLib/res/values-uz/arrays.xml b/packages/SettingsLib/res/values-uz/arrays.xml
index 164aa93b19f8..4bc995f1601b 100644
--- a/packages/SettingsLib/res/values-uz/arrays.xml
+++ b/packages/SettingsLib/res/values-uz/arrays.xml
@@ -241,7 +241,7 @@
<item msgid="2355151170975410323">"“<xliff:g id="AS_TYPED_COMMAND">adb shell dumpsys gfxinfo</xliff:g>” buyrug‘ida"</item>
</string-array>
<string-array name="debug_hw_overdraw_entries">
- <item msgid="8190572633763871652">"YOQILMAGAN"</item>
+ <item msgid="8190572633763871652">"Yoqilmagan"</item>
<item msgid="7688197031296835369">"Ortiqcha chizilgan joylarni ko‘rsatish"</item>
<item msgid="2290859360633824369">"Muayyan rangdagi hududlarni ajratib belgilash"</item>
</string-array>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 5c06c80a049c..003c45751ecc 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Máy tính bảng có thể sắp tắt (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Thiết bị có thể sắp tắt (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Còn <xliff:g id="TIME">%1$s</xliff:g> cho tới khi được sạc đầy"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> cho tới khi được sạc đầy"</string>
+ <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> nữa sẽ được sạc đầy"</string>
+ <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> nữa sẽ được sạc đầy"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Không xác định"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Đang sạc"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"đang sạc"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 460142c6d738..bcf1f2df2849 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -60,7 +60,7 @@
<string name="speed_label_medium" msgid="3175763313268941953">"适中"</string>
<string name="speed_label_fast" msgid="7715732164050975057">"快"</string>
<string name="speed_label_very_fast" msgid="2265363430784523409">"很快"</string>
- <string name="preference_summary_default_combination" msgid="8532964268242666060">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
+ <string name="preference_summary_default_combination" msgid="8532964268242666060">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="6557104142667339895">"已断开连接"</string>
<string name="bluetooth_disconnecting" msgid="8913264760027764974">"正在断开连接..."</string>
<string name="bluetooth_connecting" msgid="8555009514614320497">"正在连接..."</string>
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/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 8c0108dace69..602fe3ec12fd 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -1264,9 +1264,7 @@ public class BugreportProgressService extends Service {
}
return new Notification.Builder(context, NOTIFICATION_CHANNEL_ID)
.addExtras(sNotificationBundle)
- .setSmallIcon(
- isTv(context) ? R.drawable.ic_bug_report_black_24dp
- : com.android.internal.R.drawable.stat_sys_adb)
+ .setSmallIcon(R.drawable.ic_bug_report_black_24dp)
.setLocalOnly(true)
.setColor(context.getColor(
com.android.internal.R.color.system_notification_accent_color))
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/Android.bp b/packages/SystemUI/Android.bp
index d674be4c8fc0..37fefc2d37d7 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -76,6 +76,16 @@ android_library {
plugins: ["dagger2-compiler-2.19"],
}
+filegroup {
+ name: "SystemUI-tests-utils",
+ srcs: [
+ "tests/src/com/android/systemui/statusbar/NotificationEntryBuilder.java",
+ "tests/src/com/android/systemui/statusbar/RankingBuilder.java",
+ "tests/src/com/android/systemui/statusbar/SbnBuilder.java",
+ ],
+ path: "tests/src",
+}
+
android_library {
name: "SystemUI-tests",
manifest: "tests/AndroidManifest.xml",
diff --git a/packages/SystemUI/legacy/recents/res/values-vi/strings.xml b/packages/SystemUI/legacy/recents/res/values-vi/strings.xml
index f672a3d72dcb..aefeae92a8a4 100644
--- a/packages/SystemUI/legacy/recents/res/values-vi/strings.xml
+++ b/packages/SystemUI/legacy/recents/res/values-vi/strings.xml
@@ -34,7 +34,7 @@
<string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> bị tắt ở chế độ an toàn."</string>
<string name="recents_stack_action_button_label" msgid="1974273390109881497">"Xóa tất cả"</string>
<string name="recents_drag_hint_message" msgid="610417221848280136">"Kéo vào đây để sử dụng chế độ chia đôi màn hình"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Phân tách ngang"</string>
+ <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Chia ngang"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Phân tách dọc"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Tùy chỉnh phân tách"</string>
<string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Chia đôi màn hình lên trên"</string>
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java
index 60435d0dec35..d62c1d411cff 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java
@@ -22,7 +22,7 @@ import com.android.systemui.plugins.annotations.ProvidesInterface;
/**
* Allows for additional sensors to be retrieved from
- * {@link com.android.systemui.util.AsyncSensorManager}.
+ * {@link com.android.systemui.util.sensors.AsyncSensorManager}.
*/
@ProvidesInterface(action = SensorManagerPlugin.ACTION, version = SensorManagerPlugin.VERSION)
public interface SensorManagerPlugin extends Plugin {
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 638858a38335..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
@@ -68,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();
diff --git a/packages/SystemUI/res-keyguard/values-et/strings.xml b/packages/SystemUI/res-keyguard/values-et/strings.xml
index 7e03a5cff371..b6cac13a728d 100644
--- a/packages/SystemUI/res-keyguard/values-et/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-et/strings.xml
@@ -117,7 +117,7 @@
<string name="kg_pin_accepted" msgid="7637293533973802143">"Kood on õige."</string>
<string name="keyguard_carrier_default" msgid="4274828292998453695">"Teenus puudub."</string>
<string name="accessibility_ime_switch_button" msgid="2695096475319405612">"Vaheta sisestusmeetodit"</string>
- <string name="airplane_mode" msgid="3807209033737676010">"Lennurežiim"</string>
+ <string name="airplane_mode" msgid="3807209033737676010">"Lennukirežiim"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="7246972020562621506">"Pärast seadme taaskäivitamist tuleb sisestada muster"</string>
<string name="kg_prompt_reason_restart_pin" msgid="6303592361322290145">"Pärast seadme taaskäivitamist tuleb sisestada PIN-kood"</string>
<string name="kg_prompt_reason_restart_password" msgid="6984641181515902406">"Pärast seadme taaskäivitamist tuleb sisestada parool"</string>
diff --git a/packages/SystemUI/res/layout/auth_biometric_contents.xml b/packages/SystemUI/res/layout/auth_biometric_contents.xml
index 925e4fad103d..a1006a8396e0 100644
--- a/packages/SystemUI/res/layout/auth_biometric_contents.xml
+++ b/packages/SystemUI/res/layout/auth_biometric_contents.xml
@@ -42,7 +42,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="24dp"
- android:paddingBottom="48dp"
android:paddingTop="8dp"
android:gravity="@integer/biometric_dialog_text_gravity"
android:textSize="16sp"
@@ -52,6 +51,7 @@
android:id="@+id/biometric_icon"
android:layout_width="@dimen/biometric_dialog_biometric_icon_size"
android:layout_height="@dimen/biometric_dialog_biometric_icon_size"
+ android:paddingTop="48dp"
android:layout_gravity="center_horizontal"
android:scaleType="fitXY" />
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_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-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 538fdce944ad..4d451bc62efa 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Kanselleer"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Bevestig"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Probeer weer"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Leë gebied; tik om stawing te kanselleer"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Tik om stawing te kanselleer"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Probeer asseblief weer"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Soek tans jou gesig"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Gesig is gestaaf"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Bevestig"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Tik op Bevestig om te voltooi"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Gestaaf"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Raak die vingerafdruksensor"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Vingerafdrukikoon"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Soek tans vir jou …"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index a3ff5b4d7e83..4d0a62eeabbc 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"ይቅር"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"አረጋግጥ"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"እንደገና ይሞክሩ"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"ባዶ ክልል፣ ፈቃድን ለመሰረዝ መታ ያድርጉ"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"ማረጋገጥን ለመሰረዝ መታ ያድርጉ"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"እባክዎ እንደገና ይሞክሩ"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"መልክዎን በመፈለግ ላይ"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"መልክ ተረጋግጧል"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"ተረጋግጧል"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"ለማጠናቀቅ አረጋግጥን መታ ያድርጉ"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"የተረጋገጠ"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"የጣት አሻራ ዳሳሹን ይንኩ"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"የጣት አሻራ አዶ"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"እርስዎን በመፈለግ ላይ…"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 529ca5c12a9d..787fc8d48e8f 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"إلغاء"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"تأكيد"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"إعادة المحاولة"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"منطقة خالية، يُرجى النقر لإلغاء المصادقة."</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"انقر لإلغاء المصادقة."</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"يُرجى إعادة المحاولة."</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"جارٍ البحث عن وجهك"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"تمّت مصادقة الوجه."</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"تمّ التأكيد."</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"يمكنك النقر على \"تأكيد\" لإكمال المهمة."</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"مصادقة"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"المس زر استشعار بصمة الإصبع"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"رمز بصمة الإصبع"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"جارٍ البحث عن وجهك…"</string>
@@ -555,8 +556,8 @@
<string name="screen_pinning_description_gestural" msgid="1191513974909607884">"يؤدي هذا إلى استمرار عرض الشاشة المُختارة إلى أن تتم إزالة تثبيتها. مرّر الشاشة بسرعة للأعلى مع الاستمرار لإزالة تثبيت الشاشة."</string>
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"يؤدي هذا إلى استمرار عرض الشاشة المُختارة إلى أن تتم إزالة تثبيتها. المس مع الاستمرار زر \"نظرة عامة\" لإزالة التثبيت."</string>
<string name="screen_pinning_description_recents_invisible_accessible" msgid="6134833683151189507">"يؤدي هذا إلى استمرار عرض الشاشة المُختارة إلى أن تتم إزالة تثبيتها. المس مع الاستمرار زر \"الشاشة الرئيسية\" لإزالة التثبيت."</string>
- <string name="screen_pinning_toast" msgid="2266705122951934150">"لإزالة تثبيت هذه الشاشة، يمكنك أن تلمس مع الاستمرار زرّي \"رجوع\" و\"نظرة عامة\"."</string>
- <string name="screen_pinning_toast_recents_invisible" msgid="8252402309499161281">"لإزالة تثبيت هذه الشاشة، يمكنك أن تلمس مع الاستمرار زرّي \"رجوع\" و\"الشاشة الرئيسية\"."</string>
+ <string name="screen_pinning_toast" msgid="2266705122951934150">"لإزالة تثبيت هذه الشاشة، يمكنك النقر مع الاستمرار على زرّي \"الرجوع\" و\"النظرة العامة\"."</string>
+ <string name="screen_pinning_toast_recents_invisible" msgid="8252402309499161281">"لإزالة تثبيت هذه الشاشة، يمكنك النقر مع الاستمرار على زرّي \"الرجوع\" و\"الشاشة الرئيسية\"."</string>
<string name="screen_pinning_toast_gesture_nav" msgid="5070548776081664958">"مرّر الشاشة بسرعة للأعلى مع الاستمرار لإزالة تثبيت الشاشة."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"حسنًا"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"لا، شكرًا"</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 7d8ba648e1bb..d7feb0cc7f12 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"বাতিল কৰক"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"নিশ্চিত কৰক"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"আকৌ চেষ্টা কৰক"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"এলেকা খালী আছে, বিশ্বাসযোগ্যতা প্ৰমাণীকৰণ বাতিল কৰিবলৈ টিপক"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"বিশ্বাসযোগ্যতা প্ৰমাণীকৰণ বাতিল কৰিবলৈ টিপক"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"অনুগ্ৰহ কৰি আকৌ চেষ্টা কৰক"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"আপোনাৰ মুখমণ্ডল বিচাৰি থকা হৈছে"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"মুখমণ্ডলৰ বিশ্বাসযোগ্যতা প্ৰমাণীকৰণ কৰা হ’ল"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"নিশ্চিত কৰিলে"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"সম্পূৰ্ণ কৰিবলৈ নিশ্চিত কৰক-ত টিপক"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"বিশ্বাসযোগ্যতা প্ৰমাণীকৰণ কৰা হ’ল"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"ফিংগাৰপ্ৰিণ্ট ছেন্সৰটো স্পৰ্শ কৰক"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"ফিংগাৰপ্ৰিণ্ট আইকন"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"আপোনাৰ মুখমণ্ডল বিচাৰি আছে…"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 5ae4d2df4636..f6cd5dc2fd4a 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Ləğv et"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Təsdiq"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Yenidən cəhd edin"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Region boşdur, identifikasiyanı ləğv etmək üçün toxunun"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Doğrulanmanı ləğv etmək üçün toxunun"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Yenidən cəhd edin"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Üzünüz axtarılır"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Üz doğrulandı"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Təsdiqləndi"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Tamamlamaq üçün \"Təsdiq edin\" seçiminə toxunun"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Doğrulandı"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Barmaq izi sensoruna klikləyin"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Barmaq izi ikonası"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Siz axtarılırsınız…"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 95346e4cff82..2196c15354f5 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Otkaži"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Potvrdi"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Probaj ponovo"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Prazna oblast, dodirnite da biste otkazali potvrdu identiteta"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Dodirnite da biste otkazali potvrdu identiteta"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Probajte ponovo"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Traži se vaše lice"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Lice je potvrđeno"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Potvrđeno"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Dodirnite Potvrdi da biste završili"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Identitet je potvrđen"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Dodirnite senzor za otisak prsta"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ikona otiska prsta"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Tražimo vas…"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 36745648b051..06ee3e19c735 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Скасаваць"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Пацвердзіць"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Паўтарыць спробу"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Пустая вобласць. Націсніце, каб скасаваць аўтэнтыфікацыю"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Націсніце, каб скасаваць аўтэнтыфікацыю"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Паўтарыце спробу"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Ідзе пошук твару"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Твар распазнаны"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Пацверджана"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Націсніце \"Пацвердзіць\", каб завяршыць"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Распазнана"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Дакраніцеся да сканера адбіткаў пальцаў"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Значок адбіткаў пальцаў"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Ідзе пошук вашага твару…"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 1618e059765d..6f37a7568ea7 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Отказ"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Потвърждаване"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Нов опит"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Празна област. Докоснете, за да анулирате удостоверяването"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Докоснете, за да анулирате удостоверяването"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Моля, опитайте отново"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Лицето ви се търси"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Лицето е удостоверено"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Потвърдено"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Докоснете „Потвърждаване“ за завършване"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Удостоверено"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Докоснете сензора за отпечатъци"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Икона за отпечатък"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Търсим ви…"</string>
@@ -401,7 +402,7 @@
<string name="speed_bump_explanation" msgid="1288875699658819755">"Ппоказване на по-малко спешните известия по-долу"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Докоснете отново, за да отворите"</string>
<string name="keyguard_unlock" msgid="6035822649218712063">"Прекарайте пръст нагоре, за да отключите"</string>
- <string name="keyguard_retry" msgid="5221600879614948709">"Прекарайте пръст нагоре, за да опитате отново"</string>
+ <string name="keyguard_retry" msgid="5221600879614948709">"Плъзнете бързо нагоре, за да опитате отново"</string>
<string name="do_disclosure_generic" msgid="5615898451805157556">"Това устройство се управлява от организацията ви"</string>
<string name="do_disclosure_with_name" msgid="5640615509915445501">"Това устройство се управлява от <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="4872890986869209950">"Плъзнете с пръст от иконата, за да използвате телефона"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 6b7561bdcd3b..9c33f2be5aea 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"বাতিল করুন"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"কনফার্ম করুন"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"আবার চেষ্টা করুন"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"জায়গাটি খালি, যাচাইকরণ বাতিল করতে এখানে ট্যাপ করুন"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"যাচাইকরণ বাতিল করতে ট্যাপ করুন"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"আবার চেষ্টা করুন"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"আপনার ফেস খোঁজা হচ্ছে"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"ফেস যাচাই করা হয়েছে"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"কনফার্ম করা হয়েছে"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"সম্পূর্ণ করতে \'কনফার্ম করুন\' বোতামে ট্যাপ করুন"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"প্রমাণীকৃত"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"আঙ্গুলের ছাপের সেন্সর স্পর্শ করুন"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"আঙ্গুলের ছাপের আইকন"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"আপনার জন্য খোঁজা হচ্ছে…"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index c22ddfd56e9f..2d776265df66 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Otkaži"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Potvrdite"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Pokušaj ponovo"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Područje je prazno. Dodirnite da otkažete autentifikaciju."</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Dodirnite da otkažete autentifikaciju"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Pokušajte ponovo"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Traženje vašeg lica"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Lice je provjereno"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Potvrđeno"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Dodirnite Potvrdi da završite"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Autentificirano"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Dodirnite senzor za otisak prsta"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ikona za otisak prsta"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Tražimo vas…"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index d4147bad6c34..ee16c58909ea 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Cancel·la"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirma"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Torna-ho a provar"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Zona buida; toca per cancel·lar l\'autenticació"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Toca per cancel·lar l\'autenticació"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Torna-ho a provar"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"S\'està cercant la teva cara"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Cara autenticada"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Confirmat"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Toca Confirma per completar"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Autenticat"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Toca el sensor d\'empremtes digitals"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Icona d\'empremta digital"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"S\'està cercant la teva cara…"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 87aaa94d1dd3..bed63db83d1a 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Zrušit"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Potvrdit"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Zkusit znovu"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Prázdná oblast, klepnutím ověření zrušíte"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Klepnutím zrušíte ověření"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Zkuste to prosím znovu"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Vyhledávání obličeje"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Obličej byl ověřen"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Potvrzeno"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Ověření dokončíte klepnutím na Potvrdit"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Ověřeno"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Dotkněte se snímače otisků prstů"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ikona otisku prstu"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Hledáme vás…"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 57c934e5657d..45ef5b14f2e9 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Annuller"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Bekræft"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Prøv igen"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Tomt område. Tryk for at annullere godkendelsen."</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Tryk for at annullere godkendelsen"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Prøv igen"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Søger efter dit ansigt"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Ansigtet er godkendt"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Bekræftet"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Tryk på Bekræft for at udføre"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Godkendt"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Sæt fingeren på fingeraftrykslæseren"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ikon for fingeraftryk"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Forsøger at finde dig…"</string>
@@ -544,7 +545,7 @@
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"Dette fastholder skærmen i visningen, indtil du frigør den. Tryk på Tilbage, og hold fingeren nede for at frigøre skærmen."</string>
<string name="screen_pinning_description_recents_invisible_accessible" msgid="6134833683151189507">"Dette fastholder skærmen i visningen, indtil du frigør den. Hold Startskærm nede for at frigøre skærmen."</string>
<string name="screen_pinning_toast" msgid="2266705122951934150">"Hold knapperne Tilbage og Oversigt nede for at frigøre skærmen"</string>
- <string name="screen_pinning_toast_recents_invisible" msgid="8252402309499161281">"Hold knapperne Tilbage og Startskærm nede for at frigøre skærmen"</string>
+ <string name="screen_pinning_toast_recents_invisible" msgid="8252402309499161281">"Hold knapperne Tilbage og Hjem nede for at frigøre skærmen"</string>
<string name="screen_pinning_toast_gesture_nav" msgid="5070548776081664958">"Stryg opad, og hold fingeren nede for at frigøre denne skærm"</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"OK, det er forstået"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Nej tak"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index e1fd8db11b3a..1deceb17bf01 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Abbrechen"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Bestätigen"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Noch einmal versuchen"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Feld \"Region\" ist leer, zum Abbrechen der Authentifizierung tippen"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Zum Abbrechen der Authentifizierung tippen"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Noch einmal versuchen"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Nach deinem Gesicht wird gesucht"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Gesicht authentifiziert"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Bestätigt"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Zum Abschließen auf \"Bestätigen\" tippen"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Authentifiziert"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Berühre den Fingerabdrucksensor"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Fingerabdruck-Symbol"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Wir suchen nach dir…"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index dfb5edf97d67..2b57ab58bc77 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Ακύρωση"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Επιβεβαίωση"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Δοκιμάστε ξανά"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Κενή περιοχή, πατήστε για ακύρωση ελέγχου ταυτότητας"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Πατήστε για ακύρωση του ελέγχου ταυτότητας"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Δοκιμάστε ξανά"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Αναζήτηση για το πρόσωπό σας"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Έγινε έλεγχος ταυτότητας προσώπου"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Επιβεβαιώθηκε"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Πατήστε Επιβεβαίωση για ολοκλήρωση"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Ολοκληρώθηκε ο έλεγχος ταυτότητας"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Αγγίξτε τον αισθητήρα δακτυλικών αποτυπωμάτων"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Εικονίδιο δακτυλικών αποτυπωμάτων"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Αναζήτηση για εσάς…"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 4fd8ed9c7c09..7a6271f9710b 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Cancel"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirm"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Try again"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Empty region, tap to cancel authentication"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Tap to cancel authentication"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Please try again"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Looking for your face"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Face authenticated"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Confirmed"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Tap Confirm to complete"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Authenticated"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Touch the fingerprint sensor"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Fingerprint icon"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Looking for you…"</string>
@@ -544,7 +545,7 @@
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"This keeps it in view until you unpin. Touch &amp; hold Overview to unpin."</string>
<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_recents_invisible" msgid="8252402309499161281">"To unpin this screen, touch and 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>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 938cb0dd3b12..fe19e7c21d8c 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Cancel"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirm"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Try again"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Empty region, tap to cancel authentication"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Tap to cancel authentication"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Please try again"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Looking for your face"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Face authenticated"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Confirmed"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Tap Confirm to complete"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Authenticated"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Touch the fingerprint sensor"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Fingerprint icon"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Looking for you…"</string>
@@ -544,7 +545,7 @@
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"This keeps it in view until you unpin. Touch &amp; hold Overview to unpin."</string>
<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_recents_invisible" msgid="8252402309499161281">"To unpin this screen, touch and 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>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 4fd8ed9c7c09..7a6271f9710b 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Cancel"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirm"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Try again"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Empty region, tap to cancel authentication"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Tap to cancel authentication"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Please try again"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Looking for your face"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Face authenticated"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Confirmed"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Tap Confirm to complete"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Authenticated"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Touch the fingerprint sensor"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Fingerprint icon"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Looking for you…"</string>
@@ -544,7 +545,7 @@
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"This keeps it in view until you unpin. Touch &amp; hold Overview to unpin."</string>
<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_recents_invisible" msgid="8252402309499161281">"To unpin this screen, touch and 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>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 4fd8ed9c7c09..7a6271f9710b 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Cancel"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirm"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Try again"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Empty region, tap to cancel authentication"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Tap to cancel authentication"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Please try again"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Looking for your face"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Face authenticated"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Confirmed"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Tap Confirm to complete"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Authenticated"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Touch the fingerprint sensor"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Fingerprint icon"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Looking for you…"</string>
@@ -544,7 +545,7 @@
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"This keeps it in view until you unpin. Touch &amp; hold Overview to unpin."</string>
<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_recents_invisible" msgid="8252402309499161281">"To unpin this screen, touch and 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>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 139215aa196f..32469f20ca6b 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‏‏‎‏‎‎‎‏‎‎‏‎‎‏‎‏‎‏‏‎‏‎‎‏‎‎‏‎‏‎‏‏‏‏‎‎‎‏‎‏‏‎‏‏‎‎‏‏‎‎‏‏‎‏‎‎Cancel‎‏‎‎‏‎"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‏‎‎‎‏‎‎‏‎‎‏‎‎‏‏‏‎‏‏‎‎‎‎‎‏‎‎‎‎‏‎‎‎‎‎‎‎‎‎‎‎‏‏‏‎‎‎‎‎‏‎‎‎‏‎‎Confirm‎‏‎‎‏‎"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‎‎‏‎‏‏‏‏‎‏‏‎‏‎‎‎‎‎‏‎‏‏‎‎‎‏‏‎‎‏‏‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‎‏‏‏‏‏‎‎‎‎‏‎Try again‎‏‎‎‏‎"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‏‏‏‏‏‏‎‎‏‏‎‎‎‎‏‎‏‎‎‏‏‎‎‎‎‏‎‏‎‏‎‎‎‏‎‎‏‎‏‎‏‎‎‎‏‎‎‎‎‏‏‎‏‎‏‎Empty region, tap to cancel authentication‎‏‎‎‏‎"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‏‏‏‏‏‎‏‎‎‎‎‎‎‏‎‎‎‎‏‎‎‎‏‎‏‏‏‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‎‏‎‏‏‏‏‏‎Tap to cancel authentication‎‏‎‎‏‎"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‎‏‏‎‏‎‏‏‎‎‎‎‎‏‎‏‏‎‎‎‎‎‎‏‏‎‏‏‎‎‏‏‎‏‎‎‎‎‏‎‎‏‏‏‏‎‏‎‏‏‏‎‎‏‏‎‎Please try again‎‏‎‎‏‎"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‏‎‏‎‎‎‎‎‎‏‏‎‏‏‎‏‏‏‏‏‏‎‏‏‏‎‏‏‎‏‏‎‏‏‎‎‏‎‏‏‎‎‏‏‏‏‎‎‏‎‎‎‏‎‎‎‎Looking for your face‎‏‎‎‏‎"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‎‏‎‎‎‎‏‏‎‎‏‏‎‏‏‏‎‏‎‏‏‏‎‏‏‎‏‏‎‎‏‏‎‏‏‏‎‏‏‎‏‎‏‏‎‏‏‎‎‏‏‎‏‎‏‏‎Face authenticated‎‏‎‎‏‎"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‏‏‏‎‎‏‏‎‎‏‎‎‏‎‏‏‎‎‏‏‏‏‏‎‏‏‎‎‏‎‏‎‎‏‏‏‏‎‎‏‏‏‎‎‎‎‏‎‏‏‏‎‎‏‏‏‏‎Confirmed‎‏‎‎‏‎"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‏‎‎‎‎‎‎‏‏‏‏‎‏‏‏‎‎‎‏‏‏‏‎‎‎‏‎‏‏‎‎‏‏‏‏‏‎‎‏‏‎‏‏‎‎‎‎‎‎‏‎‎‎‎‏‎‎‎Tap Confirm to complete‎‏‎‎‏‎"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‎‎‎‏‎‎‎‏‎‎‎‏‏‎‏‏‎‏‎‎‎‏‏‏‎‎‏‎‎‎‏‎‏‏‎‏‎‎‎‎‏‎‏‏‎‎‎‏‏‏‎‎‏‏‏‎‏‎Authenticated‎‏‎‎‏‎"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‎‎‏‎‎‏‏‏‏‏‏‎‎‏‏‏‎‏‎‎‏‏‏‎‎‏‎‎‎‏‏‏‎‏‏‏‎‎‎‎‎‏‎Touch the fingerprint sensor‎‏‎‎‏‎"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‎‏‎‏‏‏‏‎‏‎‏‎‏‎‏‎‎‏‎‏‏‎‏‎‎‎‎‏‏‎‏‎‏‎‏‏‏‏‏‏‏‎‏‏‎‏‏‎‏‎‏‎‏‎‏‎‎Fingerprint icon‎‏‎‎‏‎"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‎‏‎‏‎‎‎‎‎‎‏‏‏‏‎‏‏‎‏‎‏‎‎‏‎‏‎‎‏‎‏‏‏‏‎‏‏‎‎‏‎‏‎‏‏‎‏‏‏‎‎‎‎‏‎Looking for you…‎‏‎‎‏‎"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 6adf6d5d43b1..f29aa1e0beae 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirmar"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Volver a intentarlo"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Espacio vacío; presiona para cancelar la autenticación"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Presiona para cancelar la autenticación"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Vuelve a intentarlo"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Buscando tu rostro"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Se autenticó el rostro"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Confirmado"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Presiona Confirmar para completarla"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Autenticado"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Toca el sensor de huellas digitales"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ícono de huella digital"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Autenticando tu rostro…"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index c4f34456003a..308809849bbb 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirmar"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Reintentar"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Zona vacía. Toca para cancelar la autenticación."</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Toca para cancelar la autenticación"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Vuelve a intentarlo"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Buscando tu cara"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Cara autenticada"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Confirmada"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Toca Confirmar para completar la acción"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Se ha autenticado"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Toca el sensor de huellas digitales"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Icono de huella digital"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Buscando tu cara…"</string>
@@ -544,7 +545,7 @@
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"La pantalla se mantiene visible hasta que dejas de fijarla. Para ello, mantén pulsado el botón Aplicaciones recientes."</string>
<string name="screen_pinning_description_recents_invisible_accessible" msgid="6134833683151189507">"La pantalla se mantiene visible hasta que dejas de fijarla. Para ello, mantén pulsado el botón Inicio."</string>
<string name="screen_pinning_toast" msgid="2266705122951934150">"Mantén pulsado el botón Atrás y el de aplicaciones recientes para dejar de fijar esta pantalla"</string>
- <string name="screen_pinning_toast_recents_invisible" msgid="8252402309499161281">"Mantén pulsado el botón Atrás y el de Inicio para dejar de fijar esta pantalla"</string>
+ <string name="screen_pinning_toast_recents_invisible" msgid="8252402309499161281">"Mantén pulsados los botones Atrás e Inicio para dejar de fijar esta pantalla"</string>
<string name="screen_pinning_toast_gesture_nav" msgid="5070548776081664958">"Para dejar de fijar esta pantalla, desliza el dedo hacia arriba y mantenla pulsada"</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Entendido"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"No, gracias"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index a8ad1ec6b9ea..51226abf1e84 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Tühista"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Kinnita"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Proovi uuesti"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Tühi piirkond, puudutage autentimise tühistamiseks"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Puudutage autentimise tühistamiseks"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Proovige uuesti"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Teie näo vaatamine"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Nägu on autenditud"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Kinnitatud"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Lõpuleviimiseks puudutage nuppu Kinnita"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Autenditud"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Puudutage sõrmejäljeandurit"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Sõrmejälje ikoon"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Otsitakse teid …"</string>
@@ -192,7 +193,7 @@
<string name="not_default_data_content_description" msgid="9194667237765917844">"Ei ole andmeside kasutamiseks seadistatud"</string>
<string name="cell_data_off" msgid="1051264981229902873">"Väljas"</string>
<string name="accessibility_bluetooth_tether" msgid="4102784498140271969">"Jagamine Bluetoothiga."</string>
- <string name="accessibility_airplane_mode" msgid="834748999790763092">"Lennurežiim."</string>
+ <string name="accessibility_airplane_mode" msgid="834748999790763092">"Lennukirežiim."</string>
<string name="accessibility_vpn_on" msgid="5993385083262856059">"VPN on sees."</string>
<string name="accessibility_no_sims" msgid="3957997018324995781">"SIM-kaarti pole."</string>
<string name="carrier_network_change_mode" msgid="8149202439957837762">"Operaatori võrku muudetakse"</string>
@@ -229,7 +230,7 @@
<string name="accessibility_quick_settings_airplane_off" msgid="7786329360056634412">"Lennurežiim on väljas."</string>
<string name="accessibility_quick_settings_airplane_on" msgid="6406141469157599296">"Lennurežiim on sees."</string>
<string name="accessibility_quick_settings_airplane_changed_off" msgid="66846307818850664">"Lennurežiim on välja lülitatud."</string>
- <string name="accessibility_quick_settings_airplane_changed_on" msgid="8983005603505087728">"Lennurežiim on sisse lülitatud."</string>
+ <string name="accessibility_quick_settings_airplane_changed_on" msgid="8983005603505087728">"Lennukirežiim on sisse lülitatud."</string>
<string name="accessibility_quick_settings_dnd_none_on" msgid="2960643943620637020">"täielik vaikus"</string>
<string name="accessibility_quick_settings_dnd_alarms_on" msgid="3357131899365865386">"ainult alarmid"</string>
<string name="accessibility_quick_settings_dnd" msgid="5555155552520665891">"Mitte segada."</string>
@@ -597,7 +598,7 @@
<string name="status_bar_ethernet" msgid="5044290963549500128">"Ethernet"</string>
<string name="status_bar_alarm" msgid="8536256753575881818">"Äratus"</string>
<string name="status_bar_work" msgid="6022553324802866373">"Tööprofiil"</string>
- <string name="status_bar_airplane" msgid="7057575501472249002">"Lennurežiim"</string>
+ <string name="status_bar_airplane" msgid="7057575501472249002">"Lennukirežiim"</string>
<string name="add_tile" msgid="2995389510240786221">"Paani lisamine"</string>
<string name="broadcast_tile" msgid="3894036511763289383">"Paani ülekandmine"</string>
<string name="zen_alarm_warning_indef" msgid="3482966345578319605">"Kuulete järgmist äratust kell <xliff:g id="WHEN">%1$s</xliff:g> vaid siis, kui lülitate selle enne seda välja"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 799d773fe82b..0d2863e265fb 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Utzi"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Berretsi"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Saiatu berriro"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Hutsik dago eremua. Sakatu autentifikatzeari uzteko."</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Sakatu hau autentifikazioa bertan behera uzteko"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Saiatu berriro"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Aurpegia bilatzen"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Autentifikatu da aurpegia"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Berretsita"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Amaitzeko, sakatu \"Berretsi\""</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Autentifikatuta"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Sakatu hatz-marken sentsorea"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Hatz-markaren ikonoa"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Zure bila…"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index eeb30a05de4d..e649d809db98 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -28,15 +28,15 @@
<string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> باقی مانده است"</string>
<string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> باقی مانده، براساس اشتفاده شما حدود <xliff:g id="TIME">%2$s</xliff:g> باقی مانده است"</string>
<string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> باقی مانده، حدود <xliff:g id="TIME">%2$s</xliff:g> باقی مانده است"</string>
- <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> باقی مانده است. بهینه‌سازی باتری روشن است."</string>
+ <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> باقی مانده است. «بهینه‌سازی باتری» روشن است."</string>
<string name="invalid_charger" msgid="2741987096648693172">"‏ازطریق USB شارژ نمی‌شود. از شارژر ارائه‌شده با دستگاه استفاده کنید."</string>
<string name="invalid_charger_title" msgid="2836102177577255404">"‏ازطریق USB شارژ نمی‌شود"</string>
<string name="invalid_charger_text" msgid="6480624964117840005">"از شارژر ارائه‌شده با دستگاه استفاده کنید"</string>
<string name="battery_low_why" msgid="4553600287639198111">"تنظیمات"</string>
- <string name="battery_saver_confirmation_title" msgid="2052100465684817154">"بهینه‌سازی باتری روشن شود؟"</string>
- <string name="battery_saver_confirmation_title_generic" msgid="2090922638411744540">"درباره بهینه‌سازی باتری"</string>
+ <string name="battery_saver_confirmation_title" msgid="2052100465684817154">"«بهینه‌سازی باتری» روشن شود؟"</string>
+ <string name="battery_saver_confirmation_title_generic" msgid="2090922638411744540">"درباره «بهینه‌سازی باتری»"</string>
<string name="battery_saver_confirmation_ok" msgid="7507968430447930257">"روشن کردن"</string>
- <string name="battery_saver_start_action" msgid="8187820911065797519">"بهینه‌سازی باتری را روشن کنید"</string>
+ <string name="battery_saver_start_action" msgid="8187820911065797519">"«بهینه‌سازی باتری» را روشن کنید"</string>
<string name="status_bar_settings_settings_button" msgid="3023889916699270224">"تنظیمات"</string>
<string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"Wi-Fi"</string>
<string name="status_bar_settings_auto_rotation" msgid="3790482541357798421">"چرخش خودکار صفحه"</string>
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"لغو"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"تأیید"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"امتحان مجدد"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"برای لغو احراز هویت، در قسمت خالی ضربه بزنید"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"برای لغو راستی‌آزمایی ضربه بزنید"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"لطفاً دوباره امتحان کنید"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"درحال جستجوی چهره"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"چهره احراز هویت شد"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"تأیید شد"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"برای تکمیل، روی تأیید ضربه بزنید"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"راستی‌آزمایی‌شده"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"حسگر اثر انگشت را لمس کنید"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"نماد اثر انگشت"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"درحال جستجوی شما…"</string>
@@ -450,9 +451,9 @@
<string name="user_remove_user_title" msgid="4681256956076895559">"کاربر حذف شود؟"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"همه برنامه‌ها و داده‌های این کاربر حذف می‌شود."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"حذف"</string>
- <string name="battery_saver_notification_title" msgid="8614079794522291840">"بهینه‌سازی باتری روشن است"</string>
+ <string name="battery_saver_notification_title" msgid="8614079794522291840">"«بهینه‌سازی باتری» روشن است"</string>
<string name="battery_saver_notification_text" msgid="820318788126672692">"عملکرد و اطلاعات پس‌زمینه را کاهش می‌دهد"</string>
- <string name="battery_saver_notification_action_text" msgid="132118784269455533">"بهینه‌سازی باتری را خاموش کنید"</string>
+ <string name="battery_saver_notification_action_text" msgid="132118784269455533">"«بهینه‌سازی باتری» را خاموش کنید"</string>
<string name="media_projection_dialog_text" msgid="8585357687598538511">"هنگام ضبط یا ارسال محتوا، <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> می‌تواند هرگونه اطلاعات حساس را (مانند صوت، گذرواژه، اطلاعات پرداخت، عکس و پیام) که روی صفحه‌تان نشان داده می‌شود یا از دستگاهتان پخش می‌شود ضبط کند."</string>
<string name="media_projection_dialog_service_text" msgid="3075544489835858258">"هنگام ضبط یا ارسال محتوا، ارائه‌دهنده خدمات این عملکرد می‌تواند هرگونه اطلاعات حساس (مانند صوت، گذرواژه، اطلاعات پرداخت، عکس و پیام) را که روی صفحه‌تان نشان داده می‌شود یا از دستگاهتان پخش می‌شود ضبط کند."</string>
<string name="media_projection_dialog_title" msgid="8124184308671641248">"افشای اطلاعات حساس درحین ارسال/ضبط محتوا"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 7be438a40e3b..e19575428211 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Peruuta"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Vahvista"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Yritä uudelleen"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Tyhjä alue, napauta sitä peruuttaaksesi tunnistuksen"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Peruuta todennus napauttamalla"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Yritä uudelleen"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Kasvojasi katsotaan"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Kasvot tunnistettu"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Vahvistettu"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Valitse lopuksi Vahvista"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Todennettu"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Kosketa sormenjälkitunnistinta"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Sormenjälkikuvake"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Etsitään kasvoja…"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index e8e4232063e3..4f28efa325a7 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Annuler"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirmer"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Réessayer"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Région vide, touchez l\'écran pour annuler l\'authentification"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Touchez ici pour annuler l\'authentification"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Veuillez réessayer"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"L\'appareil recherche votre visage…"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Visage authentifié"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Confirmé"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Touchez Confirmer pour terminer"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Authentifié"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Touchez le capteur d\'empreintes digitales"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Icône d\'empreinte digitale"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Recherche de votre visage…"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 3bc937807277..a51ebb1087ea 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Annuler"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirmer"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Réessayer"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Zone vide : appuyez pour annuler l\'authentification"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Appuyer pour annuler l\'authentification"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Veuillez réessayer"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Recherche de votre visage…"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Visage authentifié"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Confirmé"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Appuyez sur \"Confirmer\" pour terminer"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Authentifié"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Appuyez sur le lecteur d\'empreinte digitale"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Icône d\'empreinte digitale"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Recherche de votre visage…"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 5480eab8cf65..7a7ebf2d57c8 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirmar"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Tentar de novo"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"A rexión está baleira; tócaa para cancelar a autenticación"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Toca para cancelar a autenticación"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Téntao de novo"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Buscando a túa cara"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Autenticouse a cara"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Confirmada"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Toca Confirmar para completar o proceso"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Autenticado"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Toca o sensor de impresión dixital"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Icona de impresión dixital"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Buscándote…"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 1a15cf705d91..10aba2491be7 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"રદ કરો"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"કન્ફર્મ કરો"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"ફરી પ્રયાસ કરો"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"ખાલી વિસ્તાર, પ્રમાણીકરણ રદ કરવા માટે ટૅપ કરો"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"પ્રમાણીકરણ રદ કરવા માટે ટૅપ કરો"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"કૃપા કરી ફરી પ્રયાસ કરો"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"તમારો ચહેરો શોધી રહ્યાં છીએ"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"ચહેરાનું પ્રમાણીકરણ થયું"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"પુષ્ટિ કરી"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"પરીક્ષણ પૂર્ણ કરવા કન્ફર્મ કરોને ટૅપ કરો"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"પ્રમાણિત"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"ફિંગરપ્રિન્ટના સેન્સરને સ્પર્શ કરો"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"ફિંગરપ્રિન્ટનું આઇકન"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"તમારા માટે શોધી રહ્યાં છે..."</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 3c18b0de807b..9ce8e7d016ef 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"रद्द करें"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"पुष्टि करें"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"फिर से कोशिश करें"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"कोई चेहरा नहीं मिला, टैप करके पुष्टि की प्रक्रिया रद्द करें"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"पुष्टि की प्रक्रिया रद्द करने के लिए टैप करें"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"कृपया फिर से कोशिश करें"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"आपके चेहरे की पुष्टि की जा रही है"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"चेहरे की पुष्टि हो गई"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"पुष्टि हो गई"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"\'पुष्टि करें\' पर टैप करके पूरा करें"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"पुष्टि हो गई"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"फ़िंगरप्रिंट सेंसर को छुएं"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"फ़िंगरप्रिंट आइकॉन"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"आपको पहचान रहा है…"</string>
@@ -401,7 +402,7 @@
<string name="speed_bump_explanation" msgid="1288875699658819755">"कम अत्यावश्यक सूचनाएं नीचे दी गई हैं"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"खोलने के लिए फिर से टैप करें"</string>
<string name="keyguard_unlock" msgid="6035822649218712063">"खोलने के लिए ऊपर स्वाइप करें"</string>
- <string name="keyguard_retry" msgid="5221600879614948709">"फिर से कोशिश करने के लिए स्वाइप करें"</string>
+ <string name="keyguard_retry" msgid="5221600879614948709">"फिर से कोशिश करने के लिए ऊपर की ओर स्वाइप करें"</string>
<string name="do_disclosure_generic" msgid="5615898451805157556">"इस डिवाइस का प्रबंधन आपका संगठन करता है"</string>
<string name="do_disclosure_with_name" msgid="5640615509915445501">"इस डिवाइस के प्रबंधक <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> हैं"</string>
<string name="phone_hint" msgid="4872890986869209950">"फ़ोन के लिए आइकॉन से स्वाइप करें"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 052a0d2b07c5..9c9b0b035f0d 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Odustani"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Potvrdi"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Pokušaj ponovo"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Prazno područje, dodirnite da biste otkazali autentifikaciju"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Dodirnite da biste otkazali autentifikaciju"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Pokušajte ponovo"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Traženje lica"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Lice je autentificirano"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Potvrđeno"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Dodirnite Potvrdi za dovršetak"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Autentičnost provjerena"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Dodirnite senzor otiska prsta"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ikona otiska prsta"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Tražimo vas…"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 3e3696b45ad9..99a049ee190b 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Mégse"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Megerősítés"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Újrapróbálkozás"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Üres régió. Koppintson a hitelesítés visszavonásához"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Koppintson a hitelesítés visszavonásához"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Próbálja újra"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Arc keresése"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Arc hitelesítve"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Megerősítve"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Koppintson a Megerősítés lehetőségre"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Hitelesítve"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Érintse meg az ujjlenyomat-érzékelőt"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ujjlenyomat ikonja"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Keresem az Ön arcát…"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 4d19cec90b46..9ac0afb7cbb2 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -55,12 +55,12 @@
<string name="label_view" msgid="6304565553218192990">"Դիտել"</string>
<string name="always_use_device" msgid="4015357883336738417">"Միշտ բացել <xliff:g id="APPLICATION">%1$s</xliff:g> հավելվածը, երբ <xliff:g id="USB_DEVICE">%2$s</xliff:g> լրասարքը միացված է"</string>
<string name="always_use_accessory" msgid="3257892669444535154">"Միշտ բացել <xliff:g id="APPLICATION">%1$s</xliff:g> հավելվածը, երբ <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> լրասարքը միացված է"</string>
- <string name="usb_debugging_title" msgid="4513918393387141949">"Թույլատրե՞լ USB-ի կարգաբերումը:"</string>
+ <string name="usb_debugging_title" msgid="4513918393387141949">"Թույլատրե՞լ USB-ով վրիպազերծումը"</string>
<string name="usb_debugging_message" msgid="2220143855912376496">"Համակարգչի RSA-ի բանալի մատնահետքն է`\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"Միշտ թույլատրել այս համակարգչից"</string>
<string name="usb_debugging_allow" msgid="2272145052073254852">"Թույլատրել"</string>
- <string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"USB վրիպազերծումը արգելված է"</string>
- <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Ընթացիկ հաշվի օգտատերը չի կարող միացնել USB վրիպազերծումը: Այս գործառույթը միացնելու համար մուտք գործեք հիմնական օգտատիրոջ հաշվով:"</string>
+ <string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"USB-ով վրիպազերծումը թույլատրված չէ"</string>
+ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Ընթացիկ հաշվի միջոցով չեք կարող միացնել USB-ով վրիպազերծումը: Այս գործառույթը միացնելու համար մուտք գործեք հիմնական օգտատիրոջ հաշիվ:"</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"USB միացքն անջատված է"</string>
<string name="usb_contaminant_message" msgid="7379089091591609111">"USB միացքն անջատվել է, որպեսզի ձեր սարքը չթրջվի կամ չաղտոտվի: Այժմ USB միացքի միջոցով հնարավոր չէ միացնել այլ սարքեր:\n\nԴուք ծանուցում կստանաք, երբ այն նորից անվտանգ լինի օգտագործել:"</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"USB միացքը միացվել է՝ լիցքավորիչներն ու լրասարքերը հայտնաբերելու համար"</string>
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Չեղարկել"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Հաստատել"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Նորից փորձել"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Ոչինչ չկա։ Հպեք՝ նույնականացումը չեղարկելու համար։"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Հպեք՝ նույնականացումը չեղարկելու համար"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Նորից փորձեք"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Դեմքի նույնականացում"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Դեմքը ճանաչվեց"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Հաստատվեց"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Ավարտելու համար հպեք «Հաստատել»"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Նույնականացված է"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Հպեք մատնահետքի սկաներին"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Մատնահետքի պատկերակ"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Դեմքի ճանաչում…"</string>
@@ -539,12 +540,12 @@
<string name="accessibility_output_chooser" msgid="8185317493017988680">"Փոխել արտածման սարքը"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Էկրանն ամրացված է"</string>
<string name="screen_pinning_description" msgid="8909878447196419623">"Էկրանը կմնա տեսադաշտում, մինչև այն ապամրացնեք: Ապամրացնելու համար հպեք և պահեք Հետ և Համատեսք կոճակները:"</string>
- <string name="screen_pinning_description_recents_invisible" msgid="8281145542163727971">"Էկրանը կցուցադրվի այնքան ժամանակ, մինչև որ չապամրացնեք այն: Ապամրացնելու համար հպեք և պահեք Հետ և գլխավոր էկրանի կոճակները:"</string>
+ <string name="screen_pinning_description_recents_invisible" msgid="8281145542163727971">"Էկրանը կցուցադրվի այնքան ժամանակ, մինչև չապամրացնեք այն: Ապամրացնելու համար հպեք և պահեք «Հետ» և «Գլխավոր էկրան» կոճակները"</string>
<string name="screen_pinning_description_gestural" msgid="1191513974909607884">"Էկրանը կցուցադրվի այնքան ժամանակ, մինչև որ չապամրացնեք այն: Ապամրացնելու համար մատը սահեցրեք վեր և պահեք։"</string>
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"Էկրանը կմնա տեսադաշտում, մինչև այն ապամրացնեք: Ապամրացնելու համար հպեք և պահեք Համատեսք կոճակը:"</string>
<string name="screen_pinning_description_recents_invisible_accessible" msgid="6134833683151189507">"Էկրանը կցուցադրվի այնքան ժամանակ, մինչև որ չապամրացնեք այն: Ապամրացնելու համար հպեք և պահեք գլխավոր էկրանի կոճակը:"</string>
<string name="screen_pinning_toast" msgid="2266705122951934150">"Էկրանն ապամրացնելու համար հպեք և պահեք Հետ և Համատեսք կոճակները"</string>
- <string name="screen_pinning_toast_recents_invisible" msgid="8252402309499161281">"Էկրանն ապամրացնելու համար հպեք և պահեք Հետ և գլխավոր էկրանի կոճակները"</string>
+ <string name="screen_pinning_toast_recents_invisible" msgid="8252402309499161281">"Էկրանն ապամրացնելու համար հպեք և պահեք «Հետ» և «Գլխավոր էկրան» կոճակները"</string>
<string name="screen_pinning_toast_gesture_nav" msgid="5070548776081664958">"Էկրանն ապամրացնելու համար մատը սահեցրեք վերև և պահեք"</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Եղավ"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Ոչ"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index b18beedefcba..443e3b380094 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Batal"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Konfirmasi"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Coba lagi"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Area kosong. Ketuk untuk membatalkan autentikasi"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Ketuk untuk membatalkan autentikasi"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Coba lagi"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Mencari wajah Anda"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Wajah diautentikasi"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Dikonfirmasi"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Ketuk Konfirmasi untuk menyelesaikan"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Diautentikasi"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Sentuh sensor sidik jari"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ikon sidik jari"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Mencari wajah Anda…"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 129319da76d3..f481e48638b4 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Hætta við"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Staðfesta"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Reyna aftur"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Autt rými, ýttu til að hætta við greiningu"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Ýttu til að hætta við auðkenningu"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Reyndu aftur"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Leitar að andliti þínu"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Andlit staðfest"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Staðfest"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Ýttu á „Staðfesta“ til að ljúka"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Auðkennt"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Snertu fingrafaralesarann"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Fingrafaratákn"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Leitar að þér ..."</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index a9427e52d6ee..718608ab3964 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Annulla"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Conferma"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Riprova"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Spazio vuoto, tocca per annullare l\'autenticazione"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Tocca per annullare l\'autenticazione"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Riprova"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Ricerca del tuo volto"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Volto autenticato"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Confermato"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Tocca Conferma per completare"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Autenticazione eseguita"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Tocca il sensore di impronte digitali"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Icona dell\'impronta digitale"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"In attesa del volto…"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 52d4a13420c9..606a0978fd99 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"ביטול"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"אישור"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"ניסיון נוסף"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"אזור ריק, יש להקיש עליו כדי לבטל את האימות"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"יש להקיש כדי לבטל את האימות"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"עליך לנסות שוב"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"המערכת מחפשת את הפנים שלך"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"זיהוי הפנים בוצע"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"מאושר"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"יש להקיש על \'אישור\' לסיום"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"מאומת"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"יש לגעת בחיישן טביעות האצבע"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"סמל טביעת אצבע"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"מחפש אותך…"</string>
@@ -463,7 +464,7 @@
<string name="media_projection_dialog_service_text" msgid="3075544489835858258">"בזמן הקלטה או העברה, השירות שמספק את הפונקציה הזו יכול לקלוט מידע רגיש שמוצג במסך או מופעל מהמכשיר שלך, כולל מידע רגיש כמו אודיו, סיסמאות, פרטי תשלום, תמונות והודעות."</string>
<string name="media_projection_dialog_title" msgid="8124184308671641248">"חשיפת מידע רגיש בזמן העברה/הקלטה"</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"אל תציג שוב"</string>
- <string name="clear_all_notifications_text" msgid="814192889771462828">"נקה הכל"</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>
@@ -625,7 +626,7 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"הצג שניות בשעון בשורת הסטטוס. פעולה זו עשויה להשפיע על אורך חיי הסוללה."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"סידור מחדש של הגדרות מהירות"</string>
<string name="show_brightness" msgid="6613930842805942519">"הצג בהירות בהגדרות מהירות"</string>
- <string name="experimental" msgid="6198182315536726162">"ניסיוניות"</string>
+ <string name="experimental" msgid="6198182315536726162">"ניסיוני"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"‏האם להפעיל את ה-Bluetooth?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"‏כדי לחבר את המקלדת לטאבלט, תחילה עליך להפעיל את ה-Bluetooth."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"הפעל"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 05f254f0f80f..0bc500c2df71 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"キャンセル"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"確認"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"再試行"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"空白の領域をタップすると、認証をキャンセルできます"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"タップすると認証をキャンセルします"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"もう一度お試しください"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"顔を認証中です"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"顔を認証しました"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"確認しました"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"完了するには [確認] をタップしてください"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"認証済み"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"指紋認証センサーをタップしてください"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"指紋アイコン"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"顔を認証しています…"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 6a7460dd9f16..b3c7de798693 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"გაუქმება"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"დადასტურება"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"ხელახლა ცდა"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"დაფიქსირდა ცარიელი უბანი, შეეხეთ ამოცნობის გასაუქმებლად"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"შეეხეთ ავტორიზაციის გასაუქმებლად"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"გთხოვთ, ცადოთ ხელახლა"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"მიმდინარეობს თქვენი სახის ძებნა"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"სახის ამოცნობილია"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"დადასტურებული"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"დასასრულებლად შეეხეთ „დადასტურებას“"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"ავტორიზებულია"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"შეეხეთ თითის ანაბეჭდის სენსორს"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"თითის ანაბეჭდის ხატულა"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"მიმდინარეობს თქვენი ძიება…"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 6e0fca774809..e1fa4b2b7c8d 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Бас тарту"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Растау"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Қайталап көріңіз"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Аймақта ештеңе көрсетілмеген. Оны басып, аутентификациядан бас тартыңыз."</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Аутентификациядан бас тарту үшін түртіңіз."</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Қайталап көріңіз."</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Құрылғы бетіңізді талдап жатыр."</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Бет танылды."</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Расталды"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Аяқтау үшін \"Растау\" түймесін түртіңіз."</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Аутентификацияланған"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Саусақ ізін оқу сканерін түртіңіз"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Саусақ ізі белгішесі"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Бет ізделуде…"</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 16272fc77b2d..db93aa9a448b 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"បោះ​បង់​"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"បញ្ជាក់"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"ព្យាយាម​ម្ដង​ទៀត"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"កន្លែង​ទំនេរ សូមចុច​ដើម្បីបោះបង់​ការផ្ទៀងផ្ទាត់"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"ចុចដើម្បីបោះបង់​ការផ្ទៀងផ្ទាត់"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"សូម​ព្យាយាម​ម្ដងទៀត"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"កំពុង​ផ្ទៀងផ្ទាត់​មុខរបស់អ្នក"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"បានផ្ទៀងផ្ទាត់​មុខ"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"បានបញ្ជាក់"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"ចុច \"បញ្ជាក់\" ដើម្បីបញ្ចប់"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"បាន​ផ្ទៀងផ្ទាត់"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"ប៉ះ​ឧបករណ៍​ចាប់ស្នាម​ម្រាមដៃ"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"រូបតំណាង​ស្នាម​ម្រាមដៃ"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"កំពុងស្វែងរកអ្នក…"</string>
@@ -543,8 +544,8 @@
<string name="screen_pinning_description_gestural" msgid="1191513974909607884">"វា​នឹង​នៅតែ​បង្ហាញ រហូតទាល់​តែអ្នក​ដកការដៅ។ អូសឡើងលើ​ឱ្យជាប់ ដើម្បី​ដក​ការដៅ។"</string>
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"វា​នឹង​នៅតែ​បង្ហាញ រហូត​ទាល់​តែ​អ្នក​ដក​ការ​ដៅ។ សូម​សង្កត់​ប៊ូតុង​ទិដ្ឋភាពរួម​​ឲ្យ​ជាប់ ដើម្បី​ដក​ការ​ដៅ។"</string>
<string name="screen_pinning_description_recents_invisible_accessible" msgid="6134833683151189507">"វា​នឹង​នៅតែ​បង្ហាញ រហូត​ទាល់​តែ​អ្នក​ដក​ការដៅ។ សូម​ចុច​ប៊ូតុង​ទំព័រដើម​ឱ្យ​ជាប់ ដើម្បី​ដក​ការ​ដៅ។"</string>
- <string name="screen_pinning_toast" msgid="2266705122951934150">"ដើម្បី​ដក​ការ​ដៅ​អេក្រង់​នេះ សូម​ចុច​ប៊ូតុង​ថយ​ក្រោយ និង​ប៊ូតុង​ទិដ្ឋភាពរួម​ឱ្យ​ជាប់"</string>
- <string name="screen_pinning_toast_recents_invisible" msgid="8252402309499161281">"ដើម្បី​ដក​ការ​ដៅ​អេក្រង់​នេះ សូម​ចុច​ប៊ូតុង​ថយ​ក្រោយ និង​ប៊ូតុងទំព័រដើម​ឱ្យ​ជាប់"</string>
+ <string name="screen_pinning_toast" msgid="2266705122951934150">"ដើម្បី​ដកខ្ទាស់​អេក្រង់​នេះ សូម​ចុច​ប៊ូតុង​ថយ​ក្រោយ និង​ប៊ូតុង​ទិដ្ឋភាពរួម​ឱ្យ​ជាប់"</string>
+ <string name="screen_pinning_toast_recents_invisible" msgid="8252402309499161281">"ដើម្បី​ដកខ្ទាស់​អេក្រង់​នេះ សូម​ចុច​ប៊ូតុង​ថយ​ក្រោយ និង​ប៊ូតុងទំព័រដើម​ឱ្យ​ជាប់"</string>
<string name="screen_pinning_toast_gesture_nav" msgid="5070548776081664958">"អូសឡើងលើ​ឱ្យជាប់ ដើម្បី​ដកការដៅ​អេក្រង់​នេះ"</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"យល់​ហើយ"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"ទេ អរគុណ"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 79c4f2da77d3..98589ad29461 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"ರದ್ದುಮಾಡಿ"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"ದೃಢೀಕರಿಸಿ"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"ಖಾಲಿ ಪ್ರದೇಶ, ದೃಢೀಕರಣವನ್ನು ರದ್ದುಗೊಳಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"ದೃಢೀಕರಣವನ್ನು ರದ್ದುಗೊಳಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"ಪುನಃ ಪ್ರಯತ್ನಿಸಿ"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"ನಿಮ್ಮ ಮುಖದ ದೃಢೀಕರಣಕ್ಕಾಗಿ ನಿರೀಕ್ಷಿಸಲಾಗುತ್ತಿದೆ"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"ಮುಖವನ್ನು ದೃಢೀಕರಿಸಲಾಗಿದೆ"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"ದೃಢೀಕರಿಸಲಾಗಿದೆ"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"ಪೂರ್ಣಗೊಳಿಸಲು ದೃಢೀಕರಿಸಿ ಅನ್ನು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"ದೃಢೀಕರಿಸಲಾಗಿದೆ"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಸೆನ್ಸರ್‌‌ ಅನ್ನು ಸ್ಪರ್ಶಿಸಿ"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಐಕಾನ್"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"ನಿಮಗಾಗಿ ಹುಡುಕಲಾಗುತ್ತಿದೆ…"</string>
@@ -450,7 +451,7 @@
<string name="user_remove_user_title" msgid="4681256956076895559">"ಬಳಕೆದಾರರನ್ನು ತೆಗೆದುಹಾಕುವುದೇ?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"ಈ ಬಳಕೆದಾರರ ಎಲ್ಲಾ ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಮತ್ತು ಡೇಟಾವನ್ನು ಅಳಿಸಲಾಗುವುದು."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"ತೆಗೆದುಹಾಕಿ"</string>
- <string name="battery_saver_notification_title" msgid="8614079794522291840">"ಬ್ಯಾಟರಿ ರಕ್ಷಕ ಆನ್ ಆಗಿದೆ"</string>
+ <string name="battery_saver_notification_title" msgid="8614079794522291840">"ಬ್ಯಾಟರಿ ಸೇವರ್ ಆನ್ ಆಗಿದೆ"</string>
<string name="battery_saver_notification_text" msgid="820318788126672692">"ಕಾರ್ಯಕ್ಷಮತೆ ಮತ್ತು ಹಿನ್ನೆಲೆ ಡೇಟಾವನ್ನು ಕಡಿಮೆ ಮಾಡುತ್ತದೆ"</string>
<string name="battery_saver_notification_action_text" msgid="132118784269455533">"ಬ್ಯಾಟರಿ ಸೇವರ್‌ ಆಫ್ ಮಾಡಿ"</string>
<string name="media_projection_dialog_text" msgid="8585357687598538511">"ರೆಕಾರ್ಡ್ ಮಾಡುವಾಗ ಅಥವಾ ಕ್ಯಾಸ್ಟಿಂಗ್ ಮಾಡುವಾಗ, ಸೂಕ್ಷ್ಮ ಮಾಹಿತಿಯಂತಹ ಆಡಿಯೋ, ಪಾಸ್‌ವರ್ಡ್‌ಗಳು, ಪಾವತಿ ಮಾಹಿತಿ, ಫೋಟೋಗಳು ಮತ್ತು ಸಂದೇಶಗಳನ್ನು ಒಳಗೊಂಡಂತೆ ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ ಮೇಲೆ ಡಿಸ್‌ಪ್ಲೇ ಮಾಡಿದ ಅಥವಾ ನಿಮ್ಮ ಸಾಧನದಿಂದ ಪ್ಲೇ ಮಾಡಿದ ಯಾವುದೇ ಸೂಕ್ಷ್ಮ ಮಾಹಿತಿಯನ್ನು <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ಕ್ಯಾಪ್ಚರ್ ಮಾಡಬಹುದು."</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index b0a1671e4aa8..03c277e5a888 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -39,7 +39,7 @@
<string name="battery_saver_start_action" msgid="8187820911065797519">"절전 모드 사용 설정"</string>
<string name="status_bar_settings_settings_button" msgid="3023889916699270224">"설정"</string>
<string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"Wi-Fi"</string>
- <string name="status_bar_settings_auto_rotation" msgid="3790482541357798421">"자동 화면 회전"</string>
+ <string name="status_bar_settings_auto_rotation" msgid="3790482541357798421">"화면 자동 회전"</string>
<string name="status_bar_settings_mute_label" msgid="554682549917429396">"무시"</string>
<string name="status_bar_settings_auto_brightness_label" msgid="511453614962324674">"자동"</string>
<string name="status_bar_settings_notifications" msgid="397146176280905137">"알림"</string>
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"취소"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"확인"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"다시 시도하세요."</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"비어 있는 공간, 탭하여 인증 취소"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"탭하여 인증 취소"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"다시 시도해 주세요."</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"얼굴을 찾는 중"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"얼굴이 인증되었습니다."</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"확인함"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"완료하려면 확인을 탭하세요."</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"인증됨"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"지문 센서를 터치하세요."</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"지문 아이콘"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"찾는 중..."</string>
@@ -401,7 +402,7 @@
<string name="speed_bump_explanation" msgid="1288875699658819755">"아래에 덜 급한 알림 표시"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"다시 탭하여 열기"</string>
<string name="keyguard_unlock" msgid="6035822649218712063">"위로 스와이프하여 열기"</string>
- <string name="keyguard_retry" msgid="5221600879614948709">"위로 스와이프하여 다시 시도해 주세요."</string>
+ <string name="keyguard_retry" msgid="5221600879614948709">"위로 스와이프하여 다시 시도해 주세요"</string>
<string name="do_disclosure_generic" msgid="5615898451805157556">"조직에서 관리하는 기기입니다."</string>
<string name="do_disclosure_with_name" msgid="5640615509915445501">"<xliff:g id="ORGANIZATION_NAME">%s</xliff:g>에서 관리하는 기기입니다."</string>
<string name="phone_hint" msgid="4872890986869209950">"전화 기능을 사용하려면 아이콘에서 스와이프하세요."</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index dd985c6a40c5..c82e5b035d6a 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Жокко чыгаруу"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Ырастоо"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Кайталоо"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Бош жер калып калды, аутентификацияны жокко чыгаруу үчүн таптап коюңуз"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Аныктыгын текшерүүнү жокко чыгаруу үчүн таптаңыз"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Кайра аракет кылыңыз"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Жүзүңүз изделүүдө"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Жүздүн аныктыгы текшерилди"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Ырасталды"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Бүтүрүү үчүн \"Ырастоо\" баскычын басыңыз"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Аныктыгы текшерилди"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Манжа изинин сенсорун басыңыз"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Манжа изинин сүрөтчөсү"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Жүзүңүз изделүүдө…"</string>
@@ -401,7 +402,7 @@
<string name="speed_bump_explanation" msgid="1288875699658819755">"Анчейин шашылыш эмес эскертмелер төмөндө"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Ачуу үчүн кайра таптап коюңуз"</string>
<string name="keyguard_unlock" msgid="6035822649218712063">"Ачуу үчүн өйдө сүрүңүз"</string>
- <string name="keyguard_retry" msgid="5221600879614948709">"Кайра аракет кылуу үчүн экранды өйдө сүрүңүз"</string>
+ <string name="keyguard_retry" msgid="5221600879614948709">"Кайталоо үчүн экранды өйдө сүрүңүз"</string>
<string name="do_disclosure_generic" msgid="5615898451805157556">"Бул түзмөк уюмуңуз тарабынан башкарылат"</string>
<string name="do_disclosure_with_name" msgid="5640615509915445501">"Бул түзмөк <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> тарабынан башкарылат"</string>
<string name="phone_hint" msgid="4872890986869209950">"Сүрөтчөнү серпип телефонго өтүңүз"</string>
@@ -457,7 +458,7 @@
<string name="media_projection_dialog_service_text" msgid="3075544489835858258">"Жаздырып же тышкы экранга чыгаруу учурунда, бул функцияны аткарып жаткан колдонмо ойноткон аудиоңуз, сырсөздөрүңүз, төлөө маалыматыңыз, сүрөттөрүңүз жана билдирүүлөрүңүз сыяктуу экранда көрсөтүлгөн купуя маалыматты жаздырып калышы мүмкүн."</string>
<string name="media_projection_dialog_title" msgid="8124184308671641248">"Тышкы экранга чыгарууда/жаздырууда купуя маалыматты ачыктоо"</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"Экинчи көрсөтүлбөсүн"</string>
- <string name="clear_all_notifications_text" msgid="814192889771462828">"Бардыгын тазалап салуу"</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>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index d99db74c1fc4..4f4d29153dbb 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"ຍົກເລີກ"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"ຢືນຢັນ"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"ລອງໃໝ່"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"ພື້ນທີ່ຫວ່າງເປົ່າ, ແຕະເພື່ອຍົກເລີກການພິສູດຢືນຢັນ"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"ແຕະເພື່ອຍົກເລີກການກວດສອບຄວາມຖືກຕ້ອງ"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"ກະລຸນາລອງໃໝ່"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"ກຳລັງເບິ່ງໃບໜ້າຂອງທ່ານ"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"ພິສູດຢືນຢັນໃບໜ້າແລ້ວ"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"ຢືນຢັນແລ້ວ"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"ແຕະຢືນຢັນເພື່ອສຳເລັດ"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"ຮັບຮອງຄວາມຖືກຕ້ອງແລ້ວ"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"ແຕະໃສ່ເຊັນເຊີລາຍນິ້ວມື"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"ໄອຄອນລາຍນິ້ວມື"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"ກຳລັງຊອກຫາທ່ານ…"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 0355fc0e0e0c..ca7c89ec4847 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Atšaukti"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Patvirtinkite"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Bandyti dar kartą"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Tuščia sritis; palieskite, kad atšauktumėte autentifikavimą"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Palieskite, jei norite atšaukti autentifikavimą"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Bandykite dar kartą"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Ieškoma veido"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Veidas autentifikuotas"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Patvirtinta"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Paliesk. „Patvirtinti“, kad užbaigtumėte"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Autentifikuota"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Palieskite piršto antspaudo jutiklį"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Piršto antspaudo piktograma"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Ieškoma jūsų…"</string>
@@ -511,7 +512,7 @@
<string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"„<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>“ naudoja „<xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g>“ įrenginiui tvarkyti."</string>
<string name="monitoring_description_do_body" msgid="3639594537660975895">"Administrat. gali stebėti ir tvark. nustat., įmonės prieigos par., progr., su įreng. susietus duomenis ir įreng. vietovės inform."</string>
<string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
- <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Sužinoti daugiau"</string>
+ <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Sužinokite daugiau"</string>
<string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Esate prisijungę prie programos „<xliff:g id="VPN_APP">%1$s</xliff:g>“, kuri gali stebėti tinklo veiklą, įskaitant el. laiškus, programas ir svetaines."</string>
<string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
<string name="monitoring_description_vpn_settings" msgid="6434859242636063861">"Atidaryti VPN nustatymus"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index e6ac23579597..424e10427903 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Atcelt"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Apstiprināt"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Mēģināt vēlreiz"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Tukšs apgabals. Pieskarieties tam, lai atceltu autentificēšanu."</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Pieskarieties, lai atceltu autentifikāciju."</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Lūdzu, mēģiniet vēlreiz."</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Tiek meklēta jūsu seja"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Seja autentificēta"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Apstiprināts"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Lai pabeigtu, pieskarieties Apstiprināt"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Autentifikācija veikta"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Pieskarieties pirksta nospieduma sensoram"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Pirksta nospieduma ikona"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Notiek jūsu sejas meklēšana…"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index a529dfe6a098..dd86edecb64a 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Откажи"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Потврди"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Обиди се повторно"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Празен регион, допрете за да ја откажете проверката"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Допрете за да ја откажете проверката"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Обидете се повторно"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Го бараме вашето лице"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Лицето е проверено"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Потврдено"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Допрете „Потврди“ за да се заврши"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Проверена"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Допрете го сензорот за отпечатоци"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Икона за отпечатоци"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Ве бараме вас…"</string>
@@ -192,7 +193,7 @@
<string name="not_default_data_content_description" msgid="9194667237765917844">"Не е поставен да користи интернет"</string>
<string name="cell_data_off" msgid="1051264981229902873">"Исклучи"</string>
<string name="accessibility_bluetooth_tether" msgid="4102784498140271969">"Се поврзува со Bluetooth."</string>
- <string name="accessibility_airplane_mode" msgid="834748999790763092">"Режим на работа во авион."</string>
+ <string name="accessibility_airplane_mode" msgid="834748999790763092">"Авионски режим."</string>
<string name="accessibility_vpn_on" msgid="5993385083262856059">"VPN е вклучена."</string>
<string name="accessibility_no_sims" msgid="3957997018324995781">"Нема SIM-картичка"</string>
<string name="carrier_network_change_mode" msgid="8149202439957837762">"Променување на мрежата на операторот"</string>
@@ -347,7 +348,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Осветленост"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"Автоматски"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"Преврти ги боите"</string>
- <string name="quick_settings_color_space_label" msgid="853443689745584770">"Режим за корекција на боја"</string>
+ <string name="quick_settings_color_space_label" msgid="853443689745584770">"Режим за корекција на бои"</string>
<string name="quick_settings_more_settings" msgid="326112621462813682">"Повеќе поставки"</string>
<string name="quick_settings_done" msgid="3402999958839153376">"Готово"</string>
<string name="quick_settings_connected" msgid="1722253542984847487">"Поврзано"</string>
@@ -457,7 +458,7 @@
<string name="media_projection_dialog_service_text" msgid="3075544489835858258">"При снимањето или емитувањето, услугата што ја обезбедува функцијава може да ги сними чувствителните информации што се прикажани на вашиот екран или пуштени од вашиот уред, вклучувајќи чувствителни информации како што се аудио, лозинки, информации за плаќање, фотографии и пораки."</string>
<string name="media_projection_dialog_title" msgid="8124184308671641248">"Изложување чувствителни информации при емитување/снимање"</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"Не покажувај повторно"</string>
- <string name="clear_all_notifications_text" msgid="814192889771462828">"Исчисти сè"</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>
@@ -597,7 +598,7 @@
<string name="status_bar_ethernet" msgid="5044290963549500128">"Етернет"</string>
<string name="status_bar_alarm" msgid="8536256753575881818">"Аларм"</string>
<string name="status_bar_work" msgid="6022553324802866373">"Работен профил"</string>
- <string name="status_bar_airplane" msgid="7057575501472249002">"Режим на работа во авион"</string>
+ <string name="status_bar_airplane" msgid="7057575501472249002">"Авионски режим"</string>
<string name="add_tile" msgid="2995389510240786221">"Додај плочка"</string>
<string name="broadcast_tile" msgid="3894036511763289383">"Емитувај плочка"</string>
<string name="zen_alarm_warning_indef" msgid="3482966345578319605">"Нема да го слушнете следниот аларм <xliff:g id="WHEN">%1$s</xliff:g> освен ако претходно не го исклучите ова"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 181eb4a16942..859e65b11bb0 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"റദ്ദാക്കുക"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"സ്ഥിരീകരിക്കുക"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"വീണ്ടും ശ്രമിക്കുക"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"ശൂന്യമായ ഇടം, പരിശോധിച്ചുറപ്പിക്കൽ റദ്ദാക്കാൻ ടാപ്പ് ചെയ്യുക"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"പരിശോധിച്ചുറപ്പിക്കൽ റദ്ദാക്കാൻ ടാപ്പ് ചെയ്യുക"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"വീണ്ടും ശ്രമിക്കുക"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"നിങ്ങളുടെ മുഖത്തിന് വേണ്ടി തിരയുന്നു"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"മുഖം പരിശോധിച്ചുറപ്പിച്ചു"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"സ്ഥിരീകരിച്ചു"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"പൂർത്തിയാക്കാൻ സ്ഥിരീകരിക്കുക ടാപ്പ് ചെയ്യൂ"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"പരിശോധിച്ചുറപ്പിച്ചു"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"വിരലടയാള സെൻസർ സ്‌പർശിക്കുക"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"വിരലടയാള ഐക്കൺ"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"നിങ്ങൾക്കായി തിരയുന്നു…"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 085a0aeeb80e..3363af7a0fd9 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Цуцлах"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Баталгаажуулах"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Дахин оролдох"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Нотолгоог цуцлахын тулд хоосон бүсийг товшино уу"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Нотолгоог цуцлахын тулд товшино уу"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Дахин оролдоно уу"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Таны царайг хайж байна"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Царайг баталгаажууллаа"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Баталгаажсан"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Дуусгахын тулд баталгаажуулахыг товших"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Баталгаажуулагдсан"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Хурууны хээ мэдрэгчид хүрэх"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Хурууны хээний дүрс тэмдэг"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Таныг хайж байна…"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 4722ae9ef037..f785fe98d9c4 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"रद्द करा"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"खात्री करा"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"पुन्हा प्रयत्न करा"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"प्रदेशाचे नाव रिक्त आहे, ऑथेंटिकेशन रद्द करण्यासाठी टॅप करा"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"ऑथेंटिकेशन रद्द करण्यासाठी टॅप करा"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"कृपया पुन्हा प्रयत्न करा"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"तुमचा चेहरा शोधत आहे"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"चेहरा ऑथेंटिकेशन केलेला आहे"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"निश्चित केले"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"पूर्ण करण्यासाठी खात्री करा वर टॅप करा"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"ऑथेंटिकेशन केलेले"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"फिंगरप्रिंट सेन्सरला स्पर्श करा"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"फिंगरप्रिंट आयकन"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"तुमच्यासाठी शोधत आहे…"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 1bc7ca94792c..352c376f8a37 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Batal"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Sahkan"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Cuba lagi"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Kawasan kosong. Ketik untuk membatalkan pengesahan"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Ketik untuk membatalkan pengesahan"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Sila cuba lagi"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Mencari wajah anda"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Wajah disahkan"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Disahkan"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Ketik Sahkan untuk menyelesaikan"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Disahkan"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Sentuh penderia cap jari"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ikon cap jari"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Mencari anda…"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 51aaaa2ae954..d136042650ae 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"မလုပ်တော့"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"အတည်ပြုပါ"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"ထပ်စမ်းကြည့်ရန်"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"နေရာလွတ်၊ အထောက်အထားစိစစ်ခြင်းကို မလုပ်တော့ရန် တို့ပါ"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"အထောက်အထားစိစစ်ခြင်းကို ပယ်ဖျက်ရန် တို့ပါ"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"ထပ်စမ်းကြည့်ပါ"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"သင့်မျက်နှာကို ရှာနေသည်"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"မျက်နှာ အထောက်အထားစိစစ်ပြီးပြီ"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"အတည်ပြုပြီးပြီ"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"အပြီးသတ်ရန်အတွက် \'အတည်ပြုရန်\' ကို တို့ပါ"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"အထောက်အထားစိစစ်ပြီးပြီ"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"လက်ဗွေအာရုံခံကိရိယာကို တို့ပါ"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"လက်ဗွေ သင်္ကေတ"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"သင့်ကို ရှာဖွေနေသည်…"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index ce611e03ad6a..86f0b3804019 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Avbryt"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Bekreft"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Prøv på nytt"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Tomt område – trykk for å avbryte autentisering"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Trykk for å avbryte autentiseringen"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Prøv igjen"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Ser etter ansiktet ditt"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Ansiktet er autentisert"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Bekreftet"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Trykk på Bekreft for å fullføre"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Autentisert"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Trykk på fingeravtrykkssensoren"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ikon for fingeravtrykk"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Ser etter deg …"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 6ef85b59ea34..4c17904bac50 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"रद्द गर्नुहोस्"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"पुष्टि गर्नुहोस्"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"फेरि प्रयास गर्नुहोस्"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"खाली क्षेत्र, प्रमाणीकरण रद्द गर्न ट्याप गर्नुहोस्"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"प्रमाणीकरण रद्द गर्न ट्याप गर्नुहोस्"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"कृपया फेरि प्रयास गर्नुहोस्"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"तपाईंको अनुहार खोज्दै"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"अनुहार प्रमाणीकरण गरियो"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"पुष्टि भयो"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"पूरा गर्नका लागि पुष्टि गर्नुहोस् नामक विकल्पमा ट्याप गर्नुहोस्"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"प्रमाणीकरण गरियो"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"फिंगरप्रिन्ट सेन्सरमा छुनुहोस्‌"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"फिंगरप्रिन्ट जनाउने आइकन"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"तपाईंलाई खोज्दै…"</string>
@@ -544,7 +545,7 @@
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"तपाईंले अनपिन नगरेसम्म यसले त्यसलाई दृश्यमा कायम राख्छ। अनपिन गर्न परिदृश्य बटनलाई छोइराख्नुहोस्।"</string>
<string name="screen_pinning_description_recents_invisible_accessible" msgid="6134833683151189507">"तपाईंले अनपिन नगरेसम्म यसले त्यसलाई दृश्यमा कायम राख्छ। अनपिन गर्न गृह नामक बटनलाई छोइराख्नुहोस्।"</string>
<string name="screen_pinning_toast" msgid="2266705122951934150">"यस स्क्रिनलाई अनपनि गर्न पछाडि र परिदृश्य नामक बटनहरूलाई छोइराख्नुहोस्"</string>
- <string name="screen_pinning_toast_recents_invisible" msgid="8252402309499161281">"यस स्क्रिनलाई अनपनि गर्न पछाडि र गृह नामक बटनहरूलाई छोइराख्नुहोस्"</string>
+ <string name="screen_pinning_toast_recents_invisible" msgid="8252402309499161281">"यस स्क्रिनलाई अनपिन गर्न पछाडि र गृह नामक बटनहरूलाई छोइराख्नुहोस्"</string>
<string name="screen_pinning_toast_gesture_nav" msgid="5070548776081664958">"यो स्क्रिन अनपिन गर्न माथितिर स्वाइप गरी थिचिराख्नुहोस्"</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"बुझेँ"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"धन्यवाद पर्दैन"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 305b46a2feb6..c62c579b47b7 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Annuleren"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Bevestigen"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Opnieuw proberen"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Leeg gebied. Tik om de verificatie te annuleren."</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Tik om de verificatie te annuleren"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Probeer het opnieuw"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Er wordt naar je gezicht gezocht"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Gezicht geverifieerd"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Bevestigd"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Tik op Bevestigen om te voltooien"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Geverifieerd"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Raak de vingerafdruksensor aan"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Vingerafdrukpictogram"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Jouw gezicht zoeken…"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index e9b150c1a66e..d29592541dc1 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"କ୍ୟାନ୍ସଲ୍‍ କରନ୍ତୁ"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"ନିଶ୍ଚିତ କରନ୍ତୁ"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"ଖାଲି ଅଞ୍ଚଳ, ପ୍ରାମାଣିକତା ବାତିଲ୍ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"ପ୍ରାମାଣିକତା ବାତିଲ୍ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"ଦୟାକରି ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"ଆପଣଙ୍କର ମୁହଁକୁ ପ୍ରମାଣ କରୁଛି"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"ମୁହଁ ପ୍ରାମାଣିକତା ହୋଇଛି"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"ସୁନିଶ୍ଚିତ କରାଯାଇଛି"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"ସମ୍ପୂର୍ଣ୍ଣ କରିବାକୁ ସୁନିଶ୍ଚିତ କରନ୍ତୁରେ ଟାପ୍ କରନ୍ତୁ"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"ପ୍ରାମାଣିକତା ହୋଇଛି"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"ଟିପଚିହ୍ନ ସେନସର୍‌କୁ ଛୁଅଁନ୍ତୁ"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"ଆଙ୍ଗୁଠି ଚିହ୍ନ ଆଇକନ୍"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"ଆପଣଙ୍କୁ ଚିହ୍ନଟ କରୁଛି…"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 742080c12338..b75deb96d768 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"ਰੱਦ ਕਰੋ"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"ਪੁਸ਼ਟੀ ਕਰੋ"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"ਖਾਲੀ ਖੇਤਰ, ਪ੍ਰਮਾਣੀਕਰਨ ਰੱਦ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"ਪ੍ਰਮਾਣੀਕਰਨ ਰੱਦ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"ਕਿਰਪਾ ਕਰਕੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"ਤੁਹਾਡਾ ਚਿਹਰਾ ਲੱਭਿਆ ਜਾ ਰਿਹਾ ਹੈ"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"ਚਿਹਰਾ ਪ੍ਰਮਾਣੀਕਿਰਤ ਹੋਇਆ"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"ਪੁਸ਼ਟੀ ਕੀਤੀ ਗਈ"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"ਪੂਰਾ ਕਰਨ ਲਈ ਪੁਸ਼ਟੀ ਕਰੋ \'ਤੇ ਟੈਪ ਕਰੋ"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"ਪ੍ਰਮਾਣਿਤ ਹੋਇਆ"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਸੈਂਸਰ ਨੂੰ ਸਪੱਰਸ਼ ਕਰੋ"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਦਾ ਪ੍ਰਤੀਕ"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"ਤੁਹਾਡੀ ਪਛਾਣ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ…"</string>
@@ -401,7 +402,7 @@
<string name="speed_bump_explanation" msgid="1288875699658819755">"ਹੇਠਾਂ ਘੱਟ ਲਾਜ਼ਮੀ ਸੂਚਨਾਵਾਂ"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"ਖੋਲ੍ਹਣ ਲਈ ਦੁਬਾਰਾ ਟੈਪ ਕਰੋ"</string>
<string name="keyguard_unlock" msgid="6035822649218712063">"ਖੋਲ੍ਹਣ ਲਈ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰੋ"</string>
- <string name="keyguard_retry" msgid="5221600879614948709">"ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰਨ ਲਈ ਸਵਾਈਪ ਕਰੋ"</string>
+ <string name="keyguard_retry" msgid="5221600879614948709">"ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰਨ ਲਈ ਉੱਤੇ ਵੱਲ ਸਵਾਈਪ ਕਰੋ"</string>
<string name="do_disclosure_generic" msgid="5615898451805157556">"ਇਸ ਡੀਵਾਈਸ ਦਾ ਪ੍ਰਬੰਧਨ ਤੁਹਾਡੇ ਸੰਗਠਨ ਵੱਲੋਂ ਕੀਤਾ ਜਾਂਦਾ ਹੈ"</string>
<string name="do_disclosure_with_name" msgid="5640615509915445501">"ਇਹ ਡੀਵਾਈਸ <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> ਵੱਲੋਂ ਪ੍ਰਬੰਧਿਤ ਕੀਤਾ ਗਿਆ ਹੈ"</string>
<string name="phone_hint" msgid="4872890986869209950">"ਫ਼ੋਨ ਲਈ ਪ੍ਰਤੀਕ ਤੋਂ ਸਵਾਈਪ ਕਰੋ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 4be1a0f3aa17..d372b3d2e2c0 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Anuluj"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Potwierdź"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Spróbuj jeszcze raz"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Pusty obszar, kliknij, by anulować uwierzytelnianie"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Kliknij, by anulować uwierzytelnianie"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Spróbuj ponownie"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Szukam Twojej twarzy"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Twarz rozpoznana"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Potwierdzono"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Aby zakończyć, kliknij Potwierdź"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Uwierzytelniono"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Dotknij czytnika linii papilarnych"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ikona odcisku palca"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Szukam Cię…"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 7a57049009c0..2b761577d17d 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirmar"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Tentar novamente"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Região vazia. Toque nela para cancelar a autenticação"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Toque para cancelar a autenticação"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Tente novamente"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Procurando seu rosto"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Rosto autenticado"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Confirmada"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Toque em \"Confirmar\" para concluir"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Autenticado"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Toque no sensor de impressão digital"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ícone de impressão digital"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Procurando você…"</string>
@@ -544,7 +545,7 @@
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"Ela é mantida à vista até que seja liberada. Toque em Visão geral e mantenha essa opção pressionada para liberar."</string>
<string name="screen_pinning_description_recents_invisible_accessible" msgid="6134833683151189507">"Ela é mantida à vista até que seja liberada. Toque em Início e mantenha essa opção pressionada para liberar."</string>
<string name="screen_pinning_toast" msgid="2266705122951934150">"Para liberar esta tela, mantenha os botões Voltar e Visão geral pressionados"</string>
- <string name="screen_pinning_toast_recents_invisible" msgid="8252402309499161281">"Para liberar essa tela, toque nos botões Voltar e Início e mantenha-os pressionados"</string>
+ <string name="screen_pinning_toast_recents_invisible" msgid="8252402309499161281">"Para liberar esta tela, mantenha os botões Voltar e Início pressionados"</string>
<string name="screen_pinning_toast_gesture_nav" msgid="5070548776081664958">"Para liberar esta tela, deslize para cima e pressione"</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Entendi"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Não, obrigado"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index fb45ea77ac78..17cc86f1007e 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirmar"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Tentar novamente"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Região vazia. Toque para cancelar a autenticação."</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Toque para cancelar a autenticação."</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Tente novamente."</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"A procurar o seu rosto…"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Rosto autenticado"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Confirmado"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Toque em Confirmar para concluir."</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Autenticado"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Toque no sensor de impressões digitais."</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ícone de impressão digital"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"À sua procura…"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 7a57049009c0..2b761577d17d 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirmar"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Tentar novamente"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Região vazia. Toque nela para cancelar a autenticação"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Toque para cancelar a autenticação"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Tente novamente"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Procurando seu rosto"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Rosto autenticado"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Confirmada"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Toque em \"Confirmar\" para concluir"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Autenticado"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Toque no sensor de impressão digital"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ícone de impressão digital"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Procurando você…"</string>
@@ -544,7 +545,7 @@
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"Ela é mantida à vista até que seja liberada. Toque em Visão geral e mantenha essa opção pressionada para liberar."</string>
<string name="screen_pinning_description_recents_invisible_accessible" msgid="6134833683151189507">"Ela é mantida à vista até que seja liberada. Toque em Início e mantenha essa opção pressionada para liberar."</string>
<string name="screen_pinning_toast" msgid="2266705122951934150">"Para liberar esta tela, mantenha os botões Voltar e Visão geral pressionados"</string>
- <string name="screen_pinning_toast_recents_invisible" msgid="8252402309499161281">"Para liberar essa tela, toque nos botões Voltar e Início e mantenha-os pressionados"</string>
+ <string name="screen_pinning_toast_recents_invisible" msgid="8252402309499161281">"Para liberar esta tela, mantenha os botões Voltar e Início pressionados"</string>
<string name="screen_pinning_toast_gesture_nav" msgid="5070548776081664958">"Para liberar esta tela, deslize para cima e pressione"</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Entendi"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Não, obrigado"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index e897f1645a45..db8f87debaec 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Anulați"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirmați"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Încercați din nou"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Zonă goală, atingeți pentru a anula autentificarea"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Atingeți pentru a anula autentificarea"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Încercați din nou"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Se caută chipul"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Chip autentificat"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Confirmat"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Atingeți Confirmați pentru a finaliza"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Autentificat"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Atingeți senzorul de amprente"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Pictograma amprentă"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Vă căutăm…"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index c68f81e87039..f7527d9e0cd3 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Отмена"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Подтвердить"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Повторить попытку"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Пустая область. Нажмите на нее, чтобы отменить аутентификацию."</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Нажмите, чтобы отменить аутентификацию"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Повторите попытку"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Распознавание лица"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Лицо распознано"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Подтверждено"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Нажмите \"Подтвердить\""</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Аутентификация выполнена"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Прикоснитесь к сканеру отпечатков пальцев."</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Значок отпечатка пальца"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Поиск лица…"</string>
@@ -405,7 +406,7 @@
<string name="speed_bump_explanation" msgid="1288875699658819755">"Показать менее важные уведомления"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Нажмите ещё раз, чтобы открыть"</string>
<string name="keyguard_unlock" msgid="6035822649218712063">"Проведите вверх, чтобы открыть"</string>
- <string name="keyguard_retry" msgid="5221600879614948709">"Чтобы повторить попытку, проведите по экрану вверх."</string>
+ <string name="keyguard_retry" msgid="5221600879614948709">"Чтобы повторить попытку, проведите вверх"</string>
<string name="do_disclosure_generic" msgid="5615898451805157556">"Этим устройством управляет ваша организация"</string>
<string name="do_disclosure_with_name" msgid="5640615509915445501">"Этим устройством управляет компания \"<xliff:g id="ORGANIZATION_NAME">%s</xliff:g>\""</string>
<string name="phone_hint" msgid="4872890986869209950">"Телефон: проведите от значка"</string>
@@ -549,8 +550,8 @@
<string name="screen_pinning_description_gestural" msgid="1191513974909607884">"Экран будет зафиксирован, пока вы не отмените блокировку (для этого нужно провести вверх и удерживать)."</string>
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"Приложение останется активным, пока вы не отмените блокировку, нажав и удерживая кнопку \"Обзор\"."</string>
<string name="screen_pinning_description_recents_invisible_accessible" msgid="6134833683151189507">"Приложение останется активным, пока вы не отмените блокировку, нажав и удерживая кнопку \"Главный экран\"."</string>
- <string name="screen_pinning_toast" msgid="2266705122951934150">"Чтобы отменить блокировку, нажмите и удерживайте кнопки \"Назад\" и \"Обзор\""</string>
- <string name="screen_pinning_toast_recents_invisible" msgid="8252402309499161281">"Чтобы отменить блокировку, нажмите и удерживайте кнопки \"Назад\" и \"Главный экран\""</string>
+ <string name="screen_pinning_toast" msgid="2266705122951934150">"Чтобы открепить экран, нажмите и удерживайте кнопки \"Назад\" и \"Обзор\"."</string>
+ <string name="screen_pinning_toast_recents_invisible" msgid="8252402309499161281">"Чтобы открепить экран, нажмите и удерживайте кнопки \"Назад\" и \"Главный экран\"."</string>
<string name="screen_pinning_toast_gesture_nav" msgid="5070548776081664958">"Чтобы открепить этот экран, проведите по нему вверх и задержите руку в крайнем положении."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"ОК"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Нет, спасибо"</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 2c54218778b9..c87ef5a001b5 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"අවලංගු කරන්න"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"තහවුරු කරන්න"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"නැවත උත්සාහ කරන්න"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"හිස් කලාපය, සත්‍යාපනය අවලංගු කිරීමට තට්ටු කරන්න"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"සත්‍යාපනය අවලංගු කිරීමට තට්ටු කරන්න"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"නැවත උත්සාහ කරන්න"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"ඔබේ මුහුණ සොයනු ලැබේ"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"මුහුණ සත්‍යාපන කළා"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"තහවුරු කළා"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"සම්පූර්ණ කිරීමට තහවුරු කරන්න තට්ටු කර."</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"සත්‍යාපනය විය"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"ඇඟිලි සලකුණු සංවේදකය ස්පර්ශ කරන්න"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"ඇඟිලි සලකුණු නිරූපකය"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"ඔබව සොයමින්…"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index d14f928f75a1..1e171a1ab055 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Zrušiť"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Potvrdiť"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Skúsiť znova"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Prázdna oblasť, klepnutím zrušte overenie"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Klepnutím zrušíte overenie"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Skúste to znova"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Hľadá sa vaša tvár"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Tvár bola overená"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Potvrdené"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Overenie dokončíte klepnutím na Potvrdiť"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Overené"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Dotknite sa senzora odtlačkov prstov"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ikona odtlačku prsta"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Hľadáme vás…"</string>
@@ -405,7 +406,7 @@
<string name="speed_bump_explanation" msgid="1288875699658819755">"Menej naliehavé upozornenia sa nachádzajú nižšie"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Upozornenie otvoríte opätovným klepnutím"</string>
<string name="keyguard_unlock" msgid="6035822649218712063">"Otvorte potiahnutím prstom nahor"</string>
- <string name="keyguard_retry" msgid="5221600879614948709">"Potiahnutím nahor to skúsite znova"</string>
+ <string name="keyguard_retry" msgid="5221600879614948709">"Potiahnutím nahor to skúste znova"</string>
<string name="do_disclosure_generic" msgid="5615898451805157556">"Toto zariadenie spravuje vaša organizácia"</string>
<string name="do_disclosure_with_name" msgid="5640615509915445501">"Toto zariadenie spravuje organizácia <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="4872890986869209950">"Telefón otvoríte prejdením prstom od ikony"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 63358babeb13..b342ce6c0d32 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Prekliči"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Potrdite"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Poskusi znova"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Prazno območje. Dotaknite se, da prekličete preverjanje pristnosti."</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Če želite preklicati preverjanje pristnosti, se dotaknite"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Poskusite znova"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Iskanje obraza"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Pristnost obraza je potrjena"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Potrjeno"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Za dokončanje se dotaknite »Potrdite«"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Preverjena pristnost"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Dotaknite se tipala prstnih odtisov"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ikona prstnih odtisov"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Preverjanje vašega obraza …"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index c948328f76cc..4fba77052876 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -96,7 +96,7 @@
<string name="usb_preference_title" msgid="6551050377388882787">"Opsionet e transferimit të dosjeve të USB-së"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Lidh si një lexues \"media\" (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Montoje si kamerë (PTP)"</string>
- <string name="installer_cd_button_title" msgid="2312667578562201583">"Instalo apl. \"Transferimi i skedarëve\" për \"Mac\""</string>
+ <string name="installer_cd_button_title" msgid="2312667578562201583">"Instalo \"Transferimi i skedarëve të Android\" për Mac"</string>
<string name="accessibility_back" msgid="567011538994429120">"Prapa"</string>
<string name="accessibility_home" msgid="8217216074895377641">"Faqja bazë"</string>
<string name="accessibility_menu" msgid="316839303324695949">"Menyja"</string>
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Anulo"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Konfirmo"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Provo përsëri"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Rajon bosh, trokit për të anuluar vërtetimin"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Trokit për të anuluar vërtetimin"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Provo përsëri"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Po kërkon për fytyrën tënde"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Fytyra u vërtetua"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Konfirmuar"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Trokit \"Konfirmo\" për ta përfunduar"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"U vërtetua"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Prek sensorin e gjurmës së gishtit"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ikona e gjurmës së gishtit"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Po të kërkojmë…"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 4d289dc2b428..310fc6fb7840 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Откажи"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Потврди"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Пробај поново"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Празна област, додирните да бисте отказали потврду идентитета"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Додирните да бисте отказали потврду идентитета"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Пробајте поново"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Тражи се ваше лице"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Лице је потврђено"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Потврђено"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Додирните Потврди да бисте завршили"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Идентитет је потврђен"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Додирните сензор за отисак прста"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Икона отиска прста"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Тражимо вас…"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index b891b9382268..54769cd291c5 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Avbryt"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Bekräfta"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Försök igen"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Området är tomt. Tryck för att avbryta autentiseringen"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Tryck för att avbryta autentiseringen"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Försök igen"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Söker efter ditt ansikte"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Ansiktet har autentiserats"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Bekräftat"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Slutför genom att trycka på Bekräfta"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Autentiserad"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Tryck på fingeravtryckssensorn"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ikon för fingeravtryck"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Håller utkik efter dig …"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index b37405aa19eb..3f9278a55941 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Ghairi"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Thibitisha"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Jaribu tena"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Eneo lisilo na chochote, gusa ili ughairi uthibitishaji"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Gusa ili ughairi uthibitishaji"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Tafadhali jaribu tena"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Inatafuta uso wako"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Uso umethibitishwa"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Imethibitishwa"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Gusa Thibitisha ili ukamilishe"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Umethibitishwa"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Gusa kitambua alama ya kidole"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Aikoni ya alama ya kidole"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Inakutafuta…"</string>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 2b12149e06ad..0f368899b086 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"ரத்துசெய்"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"உறுதிப்படுத்துக"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"மீண்டும் முயல்க"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"காலியான பகுதி, அங்கீகாரத்தை ரத்துசெய்யத் தட்டவும்"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"பயோமெட்ரிக் அடையாளத்தை ரத்துசெய்ய தட்டவும்"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"மீண்டும் முயலவும்"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"உங்கள் முகத்தை அங்கீகரிக்கிறது"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"முகம் அங்கீகரிக்கப்பட்டது"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"உறுதிப்படுத்தப்பட்டது"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"முடிக்க \'உறுதிப்படுத்து\' என்பதை தட்டவும்"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"அங்கீகரிக்கப்பட்டது"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"கைரேகை சென்சாரைத் தொடவும்"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"கைரேகை ஐகான்"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"உங்கள் முகத்தைத் தேடுகிறது…"</string>
@@ -506,18 +507,18 @@
<string name="monitoring_description_do_body" msgid="3639594537660975895">"உங்கள் நிர்வாகியால் அமைப்புகள், நிறுவன அணுகல், ஆப்ஸ், சாதனத்துடன் தொடர்புடைய டேட்டா, சாதன இருப்பிடத் தகவல் ஆகியவற்றைக் கண்காணிக்கவும் நிர்வகிக்கவும் முடியும்."</string>
<string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
<string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"மேலும் அறிக"</string>
- <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"<xliff:g id="VPN_APP">%1$s</xliff:g> உடன் இணைக்கப்பட்டுள்ளீர்கள். இந்தப் பயன்பாட்டால் மின்னஞ்சல்கள், பயன்பாடுகள், இணையதளங்கள் உட்பட உங்கள் நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்க முடியும்."</string>
+ <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"<xliff:g id="VPN_APP">%1$s</xliff:g> உடன் இணைக்கப்பட்டுள்ளீர்கள். இந்த ஆப்ஸால் மின்னஞ்சல்கள், பயன்பாடுகள், இணையதளங்கள் உட்பட உங்கள் நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்க முடியும்."</string>
<string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
<string name="monitoring_description_vpn_settings" msgid="6434859242636063861">"VPN அமைப்புகளைத் திற"</string>
<string name="monitoring_description_ca_cert_settings_separator" msgid="4987350385906393626">" "</string>
<string name="monitoring_description_ca_cert_settings" msgid="5489969458872997092">"நம்பகமான அனுமதிச் சான்றுகளைத் திற"</string>
<string name="monitoring_description_network_logging" msgid="7223505523384076027">"உங்கள் நிர்வாகி நெட்வொர்க் பதிவெடுத்தலை இயக்கியுள்ளார், இது சாதனத்தில் ட்ராஃபிக்கைக் கண்காணிக்கும்.\n\nமேலும் தகவலுக்கு, உங்கள் நிர்வாகியைத் தொடர்புகொள்ளவும்."</string>
- <string name="monitoring_description_vpn" msgid="4445150119515393526">"VPN இணைப்பை அமைக்க, பயன்பாட்டிற்கு அனுமதி வழங்கியுள்ளீர்கள்.\n\nஇந்தப் பயன்பாட்டால் மின்னஞ்சல்கள், பயன்பாடுகள் மற்றும் இணையதளங்கள் உட்பட, உங்கள் சாதனத்தையும் நெட்வொர்க் செயல்பாட்டையும் கண்காணிக்க முடியும்."</string>
+ <string name="monitoring_description_vpn" msgid="4445150119515393526">"VPN இணைப்பை அமைக்க, பயன்பாட்டிற்கு அனுமதி வழங்கியுள்ளீர்கள்.\n\nஇந்த ஆப்ஸால் மின்னஞ்சல்கள், பயன்பாடுகள் மற்றும் இணையதளங்கள் உட்பட, உங்கள் சாதனத்தையும் நெட்வொர்க் செயல்பாட்டையும் கண்காணிக்க முடியும்."</string>
<string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"உங்கள் பணிக் கணக்கை <xliff:g id="ORGANIZATION">%1$s</xliff:g> நிர்வகிக்கிறது.\n\nஉங்கள் நிர்வாகியால் பயன்பாடுகள், இணையதளங்கள் உட்பட உங்கள் நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்க முடியும்.\n\nமேலும் தகவலுக்கு, உங்கள் நிர்வாகியைத் தொடர்புகொள்ளவும்.\n\nஉங்கள் நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்கக்கூடிய VPN உடனும் இணைக்கப்பட்டுள்ளீர்கள்."</string>
<string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
<string name="monitoring_description_app" msgid="1828472472674709532">"மின்னஞ்சல்கள், பயன்பாடுகள், இணையதளங்கள் உட்பட உங்கள் நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்கக்கூடிய <xliff:g id="APPLICATION">%1$s</xliff:g> உடன் இணைக்கப்பட்டுள்ளீர்கள்."</string>
- <string name="monitoring_description_app_personal" msgid="484599052118316268">"<xliff:g id="APPLICATION">%1$s</xliff:g> உடன் இணைக்கப்பட்டுள்ளீர்கள். இந்தப் பயன்பாட்டால், மின்னஞ்சல்கள், பயன்பாடுகள் மற்றும் இணையதளங்கள் உட்பட உங்கள் தனிப்பட்ட நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்க முடியும்."</string>
- <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"<xliff:g id="APPLICATION">%1$s</xliff:g> உடன் இணைக்கப்பட்டுள்ளீர்கள். இந்தப் பயன்பாட்டால் மின்னஞ்சல்கள், பயன்பாடுகள், இணையதளங்கள் உட்பட உங்கள் தனிப்பட்ட நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்க முடியும்."</string>
+ <string name="monitoring_description_app_personal" msgid="484599052118316268">"<xliff:g id="APPLICATION">%1$s</xliff:g> உடன் இணைக்கப்பட்டுள்ளீர்கள். இந்த ஆப்ஸால், மின்னஞ்சல்கள், பயன்பாடுகள் மற்றும் இணையதளங்கள் உட்பட உங்கள் தனிப்பட்ட நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்க முடியும்."</string>
+ <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"<xliff:g id="APPLICATION">%1$s</xliff:g> உடன் இணைக்கப்பட்டுள்ளீர்கள். இந்த ஆப்ஸால் மின்னஞ்சல்கள், பயன்பாடுகள், இணையதளங்கள் உட்பட உங்கள் தனிப்பட்ட நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்க முடியும்."</string>
<string name="monitoring_description_app_work" msgid="4612997849787922906">"உங்கள் பணிக் கணக்கை <xliff:g id="ORGANIZATION">%1$s</xliff:g> நிர்வகிக்கிறது. மின்னஞ்சல்கள், பயன்பாடுகள், இணையதளங்கள் உட்பட உங்கள் பணி நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்கக்கூடிய <xliff:g id="APPLICATION">%2$s</xliff:g> உடன் அது இணைக்கப்பட்டுள்ளது.\n\nமேலும் தகவலுக்கு, நிர்வாகியைத் தொடர்புகொள்ளவும்."</string>
<string name="monitoring_description_app_personal_work" msgid="5664165460056859391">"உங்கள் பணிக் கணக்கை <xliff:g id="ORGANIZATION">%1$s</xliff:g> நிர்வகிக்கிறது. மின்னஞ்சல்கள், பயன்பாடுகள், இணையதளங்கள் உட்பட உங்கள் பணி நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்கக்கூடிய <xliff:g id="APPLICATION_WORK">%2$s</xliff:g> உடன் அது இணைக்கப்பட்டுள்ளது.\n\nஉங்கள் தனிப்பட்ட நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்கக்கூடிய <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g> உடனும் இணைக்கப்பட்டுள்ளீர்கள்."</string>
<string name="keyguard_indication_trust_unlocked" msgid="2712865815371519117">"TrustAgent இதைத் திறந்தே வைத்துள்ளது"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 350ccc6e2787..62fe95b52290 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"రద్దు చేయి"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"నిర్ధారించు"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"మళ్లీ ప్రయత్నించు"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"ఖాళీ ప్రదేశం, ప్రామాణీకరణను రద్దు చేయడానికి నొక్కండి"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"ప్రామాణీకరణను రద్దు చేయడానికి నొక్కండి"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"దయచేసి మళ్ళీ ప్రయత్నించండి"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"మీ ముఖాన్ని క్యాప్చర్ చేస్తోంది"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"ముఖం ప్రామాణీకరించబడింది"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"నిర్ధారించబడింది"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"పూర్తి చేయడానికి \"నిర్ధారించు\" నొక్కండి"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"ప్రామాణీకరించబడింది"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"వేలిముద్ర సెన్సార్‌ను తాకండి"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"వేలిముద్ర చిహ్నం"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"మీ కోసం చూస్తోంది…"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 9d39859c27a0..eb6810a0898a 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"ยกเลิก"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"ยืนยัน"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"ลองอีกครั้ง"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"พื้นที่ว่าง แตะเพื่อยกเลิกการตรวจสอบสิทธิ์"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"แตะเพื่อยกเลิกการตรวจสอบสิทธิ์"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"โปรดลองอีกครั้ง"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"กำลังมองหาใบหน้าของคุณ"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"ตรวจสอบสิทธิ์ใบหน้าแล้ว"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"ยืนยันแล้ว"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"แตะยืนยันเพื่อดำเนินการให้เสร็จสมบูรณ์"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"ตรวจสอบสิทธิ์แล้ว"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"แตะเซ็นเซอร์ลายนิ้วมือ"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"ไอคอนลายนิ้วมือ"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"กำลังหาใบหน้าคุณ…"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 56da2329ab5c..f6abdcbcbada 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Kanselahin"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Kumpirmahin"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Subukang muli"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Walang laman na rehiyon, mag-tap para kanselahin ang pag-authenticate"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"I-tap para kanselahin ang pag-authenticate"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Pakisubukan ulit"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Hinahanap ang iyong mukha"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Na-authenticate ang mukha"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Nakumpirma"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"I-tap ang Kumpirmahin para kumpletuhin"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Na-authenticate"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Pindutin ang fingerprint sensor"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Icon ng fingerprint"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Hinahanap ka…"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 257b9decaeb2..b5e17023b0e5 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"İptal"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Onaylayın"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Tekrar dene"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Boş alan, yetkilendirmeyi iptal etmek için dokunun"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Kimlik doğrulama işlemini iptal etmek için dokunun"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Lütfen tekrar deneyin"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Yüzünüz aranıyor"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Yüz kimliği doğrulandı"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Onaylandı"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Tamamlamak için Onayla\'ya dokunun"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Kimliği Doğrulandı"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Parmak izi sensörüne dokunun"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Parmak izi simgesi"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Yüzünüz tanınmaya çalışılıyor…"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index d82acf7de9af..36bec1e031b4 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Скасувати"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Підтвердити"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Повторити спробу"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Порожнє місце, торкніться, щоб скасувати автентифікацію"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Натисніть, щоб скасувати автентифікацію"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Повторіть спробу"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Триває розпізнавання обличчя"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Обличчя автентифіковано"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Підтверджено"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Щоб завершити, натисніть \"Підтвердити\""</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Автентифіковано"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Торкніться сканера відбитків пальців"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Значок відбитка пальця"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Пошук обличчя…"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 7d4f177b6c1c..5fceabe93f2f 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"منسوخ کریں"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"تصدیق کریں"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"دوبارہ کوشش کریں"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"خالی جگہیں، تصدیق کو منسوخ کرنے کے لیے تھپتھپائیں"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"تصدیق کو منسوخ کرنے کے لیے تھپتھپائیں"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"براہ کرم دوبارہ کوشش کریں"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"آپ کا چہرہ تلاش کیا جا رہا ہے"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"چہرے کی تصدیق ہو گئی"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"تصدیق شدہ"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"مکمل کرنے کیلئے \'تصدیق کریں\' تھپتھپائیں"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"تصدیق کردہ"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"فنگر پرنٹ سینسر پر ٹچ کریں"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"فنگر پرنٹ آئیکن"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"آپ کے لیے تلاش کیا جا رہا ہے…"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index eaca944ec22d..37651a5c000d 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Bekor qilish"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"OK"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Qayta urinish"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Boʻsh hudud, tekshiruvni bekor qilish uchun bosing"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Tekshiruvni bekor qilish uchun bosing"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Qayta urining"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Yuz tekshirilmoqda"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Yuzingiz aniqlandi"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Tasdiqlangan"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Tasdiqlash uchun tegining"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Tasdiqlandi"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Barmoq izi skaneriga tegining"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Barmoq izi belgisi"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Yuzingiz tekshirilmoqda…"</string>
@@ -401,7 +402,7 @@
<string name="speed_bump_explanation" msgid="1288875699658819755">"Kam ahamiyatli bildirishnomalarni pastda ko‘rsatish"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Ochish uchun yana bosing"</string>
<string name="keyguard_unlock" msgid="6035822649218712063">"Ochish uchun tepaga suring"</string>
- <string name="keyguard_retry" msgid="5221600879614948709">"Tepaga suring va qayta urining"</string>
+ <string name="keyguard_retry" msgid="5221600879614948709">"Qayta urinish uchun tepaga suring"</string>
<string name="do_disclosure_generic" msgid="5615898451805157556">"Bu – tashkilotingiz tomonidan boshqariladigan qurilma"</string>
<string name="do_disclosure_with_name" msgid="5640615509915445501">"Bu – <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> tomonidan boshqariladigan qurilma"</string>
<string name="phone_hint" msgid="4872890986869209950">"Telefonni ochish uchun suring"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index d3caa2b2c75d..f6782659f1ed 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Hủy"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Xác nhận"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Thử lại"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Vùng trống, nhấn để hủy quá trình xác thực"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Nhấn để hủy quá trình xác thực"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Vui lòng thử lại"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Đang tìm khuôn mặt của bạn"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Đã xác thực khuôn mặt"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Ðã xác nhận"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Nhấn vào Xác nhận để hoàn tất"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Đã xác thực"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Chạm vào cảm biến vân tay"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Biểu tượng vân tay"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Đang tìm kiếm bạn…"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index ca72b08231c2..05509f65047e 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"取消"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"确认"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"重试"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"空白区域,点按即可取消身份验证"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"点按即可取消身份验证"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"请重试"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"正在查找您的面孔"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"面孔身份验证成功"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"已确认"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"点按“确认”即可完成"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"已经过身份验证"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"请触摸指纹传感器"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"指纹图标"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"正在查找您的面孔…"</string>
@@ -376,7 +377,7 @@
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4453017157391574402">"在日出时关闭"</string>
<string name="quick_settings_night_secondary_label_on_at" msgid="6256314040368487637">"在<xliff:g id="TIME">%s</xliff:g> 开启"</string>
<string name="quick_settings_secondary_label_until" msgid="2749196569462600150">"直到<xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_ui_mode_night_label" msgid="3419947801072692538">"深色主题背景"</string>
+ <string name="quick_settings_ui_mode_night_label" msgid="3419947801072692538">"深色主题"</string>
<string name="quick_settings_ui_mode_night_label_battery_saver" msgid="7438725724589758362">"深色主题背景\n省电模式"</string>
<string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC 已停用"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 128e1ca76fb0..b57e61fa0cf6 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"取消"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"確認"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"請再試一次"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"空白區域,輕按即可取消驗證"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"輕按即可取消驗證"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"請再試一次"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"正在尋找您的臉孔"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"臉孔已經驗證"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"已確認"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"輕按 [確定] 以完成"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"驗證咗"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"請輕觸指紋感應器"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"指紋圖示"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"正在搜尋您的臉孔…"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 457206dc9dc1..14fedbbad2c5 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"取消"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"確認"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"再試一次"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"空白的區域,輕觸即可取消驗證"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"輕觸即可取消驗證"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"請再試一次"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"正在尋找你的臉孔"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"臉孔驗證成功"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"確認完畢"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"輕觸 [確認] 完成驗證設定"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"已通過驗證"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"請輕觸指紋感應器"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"指紋圖示"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"正在尋找你的臉孔…"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 2306e4ab23fa..c158c773b03d 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -119,12 +119,13 @@
<string name="cancel" msgid="6442560571259935130">"Khansela"</string>
<string name="biometric_dialog_confirm" msgid="6468457350041712674">"Qinisekisa"</string>
<string name="biometric_dialog_try_again" msgid="1900185172633183201">"Zama futhi"</string>
- <string name="biometric_dialog_empty_space_description" msgid="7997936968009073717">"Isifunda esingenalutho, thepha ukuze ukhansele ukufakazela ubuqiniso"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="6337699671577692511">"Thepha ukuze ukhansele ukufakazela ubuqiniso"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4497694707475970790">"Sicela uzame futhi"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="4512727754496228488">"Ifuna ubuso bakho"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="595380451325425259">"Ubuso bufakazelwe ubuqiniso"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Kuqinisekisiwe"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Thepha okuthi Qinisekisa ukuze uqedele"</string>
+ <string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Kugunyaziwe"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Thinta inzwa yesigxivizo somunwe"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Isithonjana sezigxivizo zeminwe"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Kufunwa wena…"</string>
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/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index 82287873f5ad..00e8b535ccac 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -225,14 +225,16 @@ public class ActivityManagerWrapper {
runner = new IRecentsAnimationRunner.Stub() {
@Override
public void onAnimationStart(IRecentsAnimationController controller,
- RemoteAnimationTarget[] apps, Rect homeContentInsets,
- Rect minimizedHomeBounds) {
+ RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
+ Rect homeContentInsets, Rect minimizedHomeBounds) {
final RecentsAnimationControllerCompat controllerCompat =
new RecentsAnimationControllerCompat(controller);
final RemoteAnimationTargetCompat[] appsCompat =
RemoteAnimationTargetCompat.wrap(apps);
+ final RemoteAnimationTargetCompat[] wallpapersCompat =
+ RemoteAnimationTargetCompat.wrap(wallpapers);
animationHandler.onAnimationStart(controllerCompat, appsCompat,
- homeContentInsets, minimizedHomeBounds);
+ wallpapersCompat, homeContentInsets, minimizedHomeBounds);
}
@Override
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java
index 3ae2df5b97bf..2797042ac160 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java
@@ -16,9 +16,10 @@
package com.android.systemui.shared.system;
+import android.content.ComponentName;
import android.content.pm.ParceledListSlice;
import android.graphics.Rect;
-import android.os.RemoteException;
+import android.view.DisplayInfo;
import android.view.IPinnedStackController;
import android.view.IPinnedStackListener;
@@ -32,62 +33,132 @@ import java.util.List;
* previously set listener.
*/
public class PinnedStackListenerForwarder extends IPinnedStackListener.Stub {
- private List<IPinnedStackListener> mListeners = new ArrayList<>();
+ private List<PinnedStackListener> mListeners = new ArrayList<>();
/** Adds a listener to receive updates from the WindowManagerService. */
- public void addListener(IPinnedStackListener listener) {
+ public void addListener(PinnedStackListener listener) {
mListeners.add(listener);
}
/** Removes a listener so it will no longer receive updates from the WindowManagerService. */
- public void removeListener(IPinnedStackListener listener) {
+ public void removeListener(PinnedStackListener listener) {
mListeners.remove(listener);
}
@Override
- public void onListenerRegistered(IPinnedStackController controller) throws RemoteException {
- for (IPinnedStackListener listener : mListeners) {
+ public void onListenerRegistered(IPinnedStackController controller) {
+ for (PinnedStackListener listener : mListeners) {
listener.onListenerRegistered(controller);
}
}
@Override
- public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds, Rect animatingBounds,
- boolean fromImeAdjustment, boolean fromShelfAdjustment, int displayRotation)
- throws RemoteException {
- for (IPinnedStackListener listener : mListeners) {
- listener.onMovementBoundsChanged(
- insetBounds, normalBounds, animatingBounds,
- fromImeAdjustment, fromShelfAdjustment, displayRotation);
+ public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment,
+ boolean fromShelfAdjustment) {
+ for (PinnedStackListener listener : mListeners) {
+ listener.onMovementBoundsChanged(animatingBounds, fromImeAdjustment,
+ fromShelfAdjustment);
}
}
@Override
- public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) throws RemoteException {
- for (IPinnedStackListener listener : mListeners) {
+ public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
+ for (PinnedStackListener listener : mListeners) {
listener.onImeVisibilityChanged(imeVisible, imeHeight);
}
}
@Override
- public void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight)
- throws RemoteException {
- for (IPinnedStackListener listener : mListeners) {
+ public void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight) {
+ for (PinnedStackListener listener : mListeners) {
listener.onShelfVisibilityChanged(shelfVisible, shelfHeight);
}
}
@Override
- public void onMinimizedStateChanged(boolean isMinimized) throws RemoteException {
- for (IPinnedStackListener listener : mListeners) {
+ public void onMinimizedStateChanged(boolean isMinimized) {
+ for (PinnedStackListener listener : mListeners) {
listener.onMinimizedStateChanged(isMinimized);
}
}
@Override
- public void onActionsChanged(ParceledListSlice actions) throws RemoteException {
- for (IPinnedStackListener listener : mListeners) {
+ public void onActionsChanged(ParceledListSlice actions) {
+ for (PinnedStackListener listener : mListeners) {
listener.onActionsChanged(actions);
}
}
+
+ @Override
+ public void onSaveReentrySnapFraction(ComponentName componentName, Rect bounds) {
+ for (PinnedStackListener listener : mListeners) {
+ listener.onSaveReentrySnapFraction(componentName, bounds);
+ }
+ }
+
+ @Override
+ public void onResetReentrySnapFraction(ComponentName componentName) {
+ for (PinnedStackListener listener : mListeners) {
+ listener.onResetReentrySnapFraction(componentName);
+ }
+ }
+
+ @Override
+ public void onDisplayInfoChanged(DisplayInfo displayInfo) {
+ for (PinnedStackListener listener : mListeners) {
+ listener.onDisplayInfoChanged(displayInfo);
+ }
+ }
+
+ @Override
+ public void onConfigurationChanged() {
+ for (PinnedStackListener listener : mListeners) {
+ listener.onConfigurationChanged();
+ }
+ }
+
+ @Override
+ public void onAspectRatioChanged(float aspectRatio) {
+ for (PinnedStackListener listener : mListeners) {
+ listener.onAspectRatioChanged(aspectRatio);
+ }
+ }
+
+ @Override
+ public void onPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect bounds) {
+ for (PinnedStackListener listener : mListeners) {
+ listener.onPrepareAnimation(sourceRectHint, aspectRatio, bounds);
+ }
+ }
+
+ /**
+ * A counterpart of {@link IPinnedStackListener} with empty implementations.
+ * Subclasses can ignore those methods they do not intend to take action upon.
+ */
+ public static class PinnedStackListener {
+ public void onListenerRegistered(IPinnedStackController controller) {}
+
+ public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment,
+ boolean fromShelfAdjustment) {}
+
+ public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {}
+
+ public void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight) {}
+
+ public void onMinimizedStateChanged(boolean isMinimized) {}
+
+ public void onActionsChanged(ParceledListSlice actions) {}
+
+ public void onSaveReentrySnapFraction(ComponentName componentName, Rect bounds) {}
+
+ public void onResetReentrySnapFraction(ComponentName componentName) {}
+
+ public void onDisplayInfoChanged(DisplayInfo displayInfo) {}
+
+ public void onConfigurationChanged() {}
+
+ public void onAspectRatioChanged(float aspectRatio) {}
+
+ public void onPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect bounds) {}
+ }
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java
index 579858a4f9b4..2c99c5c84015 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java
@@ -21,12 +21,12 @@ import android.graphics.Rect;
import com.android.systemui.shared.recents.model.ThumbnailData;
public interface RecentsAnimationListener {
-
/**
* Called when the animation into Recents can start. This call is made on the binder thread.
*/
void onAnimationStart(RecentsAnimationControllerCompat controller,
- RemoteAnimationTargetCompat[] apps, Rect homeContentInsets, Rect minimizedHomeBounds);
+ RemoteAnimationTargetCompat[] apps, RemoteAnimationTargetCompat[] wallpapers,
+ Rect homeContentInsets, Rect minimizedHomeBounds);
/**
* Called when the animation into Recents was canceled. This call is made on the binder thread.
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
index 61be076ac48a..02e509eef25f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
@@ -45,9 +45,12 @@ public class RemoteAnimationAdapterCompat {
return new IRemoteAnimationRunner.Stub() {
@Override
public void onAnimationStart(RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers,
final IRemoteAnimationFinishedCallback finishedCallback) {
final RemoteAnimationTargetCompat[] appsCompat =
RemoteAnimationTargetCompat.wrap(apps);
+ final RemoteAnimationTargetCompat[] wallpapersCompat =
+ RemoteAnimationTargetCompat.wrap(wallpapers);
final Runnable animationFinishedCallback = new Runnable() {
@Override
public void run() {
@@ -59,7 +62,8 @@ public class RemoteAnimationAdapterCompat {
}
}
};
- remoteAnimationAdapter.onAnimationStart(appsCompat, animationFinishedCallback);
+ remoteAnimationAdapter.onAnimationStart(appsCompat, wallpapersCompat,
+ animationFinishedCallback);
}
@Override
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java
index 5a85df967197..33372f6bd0b9 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java
@@ -17,6 +17,7 @@
package com.android.systemui.shared.system;
public interface RemoteAnimationRunnerCompat {
- void onAnimationStart(RemoteAnimationTargetCompat[] apps, Runnable finishedCallback);
+ void onAnimationStart(RemoteAnimationTargetCompat[] apps,
+ RemoteAnimationTargetCompat[] wallpapers, Runnable finishedCallback);
void onAnimationCancelled();
} \ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
index 221782e950d0..ca88f13932ad 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
@@ -68,7 +68,7 @@ public class RemoteAnimationTargetCompat {
public static RemoteAnimationTargetCompat[] wrap(RemoteAnimationTarget[] apps) {
final RemoteAnimationTargetCompat[] appsCompat =
- new RemoteAnimationTargetCompat[apps.length];
+ new RemoteAnimationTargetCompat[apps != null ? apps.length : 0];
for (int i = 0; i < apps.length; i++) {
appsCompat[i] = new RemoteAnimationTargetCompat(apps[i]);
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
index 9ba21a328ca4..e80b43739557 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
@@ -142,7 +142,9 @@ public class SyncRtSurfaceTransactionApplierCompat {
public static void applyParams(TransactionCompat t,
SyncRtSurfaceTransactionApplierCompat.SurfaceParams params) {
t.setMatrix(params.surface, params.matrix);
- t.setWindowCrop(params.surface, params.windowCrop);
+ if (params.windowCrop != null) {
+ t.setWindowCrop(params.surface, params.windowCrop);
+ }
t.setAlpha(params.surface, params.alpha);
t.setLayer(params.surface, params.layer);
t.setCornerRadius(params.surface, params.cornerRadius);
@@ -187,14 +189,14 @@ public class SyncRtSurfaceTransactionApplierCompat {
* @param surface The surface to modify.
* @param alpha Alpha to apply.
* @param matrix Matrix to apply.
- * @param windowCrop Crop to apply.
+ * @param windowCrop Crop to apply, only applied if not {@code null}
*/
public SurfaceParams(SurfaceControlCompat surface, float alpha, Matrix matrix,
Rect windowCrop, int layer, float cornerRadius) {
this.surface = surface;
this.alpha = alpha;
this.matrix = new Matrix(matrix);
- this.windowCrop = new Rect(windowCrop);
+ this.windowCrop = windowCrop != null ? new Rect(windowCrop) : null;
this.layer = layer;
this.cornerRadius = cornerRadius;
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
index 794c30a7c7c1..9f1a1fafeec6 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
@@ -27,12 +27,12 @@ import android.graphics.Rect;
import android.os.Handler;
import android.os.RemoteException;
import android.util.Log;
-import android.view.IPinnedStackListener;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
import com.android.systemui.shared.recents.view.RecentsTransition;
+import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener;
public class WindowManagerWrapper {
@@ -212,7 +212,7 @@ public class WindowManagerWrapper {
* Adds a pinned stack listener, which will receive updates from the window manager service
* along with any other pinned stack listeners that were added via this method.
*/
- public void addPinnedStackListener(IPinnedStackListener listener) throws RemoteException {
+ public void addPinnedStackListener(PinnedStackListener listener) throws RemoteException {
mPinnedStackListenerForwarder.addListener(listener);
WindowManagerGlobal.getWindowManagerService().registerPinnedStackListener(
DEFAULT_DISPLAY, mPinnedStackListenerForwarder);
@@ -221,7 +221,7 @@ public class WindowManagerWrapper {
/**
* Removes a pinned stack listener.
*/
- public void removePinnedStackListener(IPinnedStackListener listener) {
+ public void removePinnedStackListener(PinnedStackListener listener) {
mPinnedStackListenerForwarder.removeListener(listener);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/ActivityBinder.java b/packages/SystemUI/src/com/android/systemui/ActivityBinder.java
new file mode 100644
index 000000000000..2c8a67270d94
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/ActivityBinder.java
@@ -0,0 +1,44 @@
+/*
+ * 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.app.Activity;
+
+import com.android.systemui.tuner.TunerActivity;
+
+import dagger.Binds;
+import dagger.Module;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.IntoMap;
+
+/**
+ * Services and Activities that are injectable should go here.
+ */
+@Module
+public abstract class ActivityBinder {
+ /** Inject into TunerActivity. */
+ @Binds
+ @IntoMap
+ @ClassKey(TunerActivity.class)
+ public abstract Activity bindTunerActivity(TunerActivity activity);
+
+ /** Inject into ForegroundServicesDialog. */
+ @Binds
+ @IntoMap
+ @ClassKey(ForegroundServicesDialog.class)
+ public abstract Activity bindForegroundServicesDialog(ForegroundServicesDialog activity);
+}
diff --git a/core/java/android/view/Transaction.aidl b/packages/SystemUI/src/com/android/systemui/ComponentBinder.java
index 8d24b437e044..3b35c61e8eb2 100644
--- a/core/java/android/view/Transaction.aidl
+++ b/packages/SystemUI/src/com/android/systemui/ComponentBinder.java
@@ -14,6 +14,18 @@
* limitations under the License.
*/
-package android.view.SurfaceControl;
+package com.android.systemui;
-parcelable Transaction;
+import dagger.Binds;
+import dagger.Module;
+
+/**
+ * Dagger Module that collects related sub-modules together.
+ */
+@Module(includes = {ActivityBinder.class, ServiceBinder.class, SystemUIBinder.class})
+public abstract class ComponentBinder {
+ /** */
+ @Binds
+ public abstract ContextComponentHelper bindComponentHelper(
+ ContextComponentResolver componentHelper);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/ContextComponentHelper.java b/packages/SystemUI/src/com/android/systemui/ContextComponentHelper.java
index 5fe5792219c3..2cf0f8dafcad 100644
--- a/packages/SystemUI/src/com/android/systemui/ContextComponentHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ContextComponentHelper.java
@@ -16,6 +16,7 @@
package com.android.systemui;
+import android.app.Activity;
import android.app.Service;
/**
@@ -23,6 +24,9 @@ import android.app.Service;
*/
public interface ContextComponentHelper {
/** Turns a classname into an instance of the class or returns null. */
+ Activity resolveActivity(String className);
+
+ /** Turns a classname into an instance of the class or returns null. */
Service resolveService(String className);
/** Turns a classname into an instance of the class or returns null. */
diff --git a/packages/SystemUI/src/com/android/systemui/ContextComponentResolver.java b/packages/SystemUI/src/com/android/systemui/ContextComponentResolver.java
index cee21c167fa0..995263240e2d 100644
--- a/packages/SystemUI/src/com/android/systemui/ContextComponentResolver.java
+++ b/packages/SystemUI/src/com/android/systemui/ContextComponentResolver.java
@@ -16,6 +16,7 @@
package com.android.systemui;
+import android.app.Activity;
import android.app.Service;
import java.util.Map;
@@ -29,18 +30,29 @@ import javax.inject.Singleton;
*/
@Singleton
public class ContextComponentResolver implements ContextComponentHelper {
+ private final Map<Class<?>, Provider<Activity>> mActivityCreators;
private final Map<Class<?>, Provider<Service>> mServiceCreators;
private final Map<Class<?>, Provider<SystemUI>> mSystemUICreators;
@Inject
ContextComponentResolver(
+ Map<Class<?>, Provider<Activity>> activityCreators,
Map<Class<?>, Provider<Service>> serviceCreators,
Map<Class<?>, Provider<SystemUI>> systemUICreators) {
+ mActivityCreators = activityCreators;
mServiceCreators = serviceCreators;
mSystemUICreators = systemUICreators;
}
/**
+ * Looks up the Activity class name to see if Dagger has an instance of it.
+ */
+ @Override
+ public Activity resolveActivity(String className) {
+ return resolve(className, mActivityCreators);
+ }
+
+ /**
* Looks up the Service class name to see if Dagger has an instance of it.
*/
@Override
@@ -57,12 +69,12 @@ public class ContextComponentResolver implements ContextComponentHelper {
}
private <T> T resolve(String className, Map<Class<?>, Provider<T>> creators) {
- for (Map.Entry<Class<?>, Provider<T>> p : creators.entrySet()) {
- if (p.getKey().getName().equals(className)) {
- return p.getValue().get();
- }
+ try {
+ Class<?> clazz = Class.forName(className);
+ Provider<T> provider = creators.get(clazz);
+ return provider == null ? null : provider.get();
+ } catch (ClassNotFoundException e) {
+ return null;
}
-
- return null;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/CornerHandleView.java b/packages/SystemUI/src/com/android/systemui/CornerHandleView.java
index 6209c2c36206..a94952c5bc19 100644
--- a/packages/SystemUI/src/com/android/systemui/CornerHandleView.java
+++ b/packages/SystemUI/src/com/android/systemui/CornerHandleView.java
@@ -19,7 +19,6 @@ package com.android.systemui;
import android.animation.ArgbEvaluator;
import android.content.Context;
import android.graphics.Canvas;
-import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
@@ -28,7 +27,6 @@ import android.util.DisplayMetrics;
import android.view.ContextThemeWrapper;
import android.view.View;
-import com.android.internal.graphics.ColorUtils;
import com.android.settingslib.Utils;
/**
@@ -109,7 +107,6 @@ public class CornerHandleView extends View {
mPaint.setColor((int) ArgbEvaluator.getInstance().evaluate(darkIntensity,
mLightColor,
mDarkColor));
- updateShadow();
if (getVisibility() == VISIBLE) {
invalidate();
}
@@ -121,21 +118,6 @@ public class CornerHandleView extends View {
canvas.drawPath(mPath, mPaint);
}
- private void updateShadow() {
- if (ColorUtils.calculateLuminance(mPaint.getColor()) > 0.7f) {
- mPaint.setShadowLayer(/** radius */ 5,/** shadowDx */ 0, /** shadowDy */ -1,
- /** color */ ColorUtils.setAlphaComponent(/** color */ Color.BLACK,
- /** alpha */ 102));
- } else {
- mPaint.setShadowLayer(/** radius */ 0, /** shadowDx */ 0, /** shadowDy */ 0,
- /** color */ Color.TRANSPARENT);
- }
-
- if (getVisibility() == VISIBLE) {
- invalidate();
- }
- }
-
private static float convertDpToPixel(float dp, Context context) {
return dp * ((float) context.getResources().getDisplayMetrics().densityDpi
/ DisplayMetrics.DENSITY_DEFAULT);
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index bd5b9c724eb3..7771f8655128 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -15,6 +15,7 @@
package com.android.systemui;
import android.annotation.Nullable;
+import android.app.AlarmManager;
import android.app.INotificationManager;
import android.content.res.Configuration;
import android.hardware.SensorPrivacyManager;
@@ -110,10 +111,10 @@ import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.tuner.TunablePadding.TunablePaddingService;
import com.android.systemui.tuner.TunerService;
-import com.android.systemui.util.AsyncSensorManager;
import com.android.systemui.util.leak.GarbageMonitor;
import com.android.systemui.util.leak.LeakDetector;
import com.android.systemui.util.leak.LeakReporter;
+import com.android.systemui.util.sensors.AsyncSensorManager;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -314,24 +315,15 @@ public class Dependency {
@Inject Lazy<INotificationManager> mINotificationManager;
@Inject Lazy<FalsingManager> mFalsingManager;
@Inject Lazy<SysUiState> mSysUiStateFlagsContainer;
+ @Inject Lazy<AlarmManager> mAlarmManager;
@Inject
public Dependency() {
}
-
/**
* Initialize Depenency.
*/
- public static void initDependencies(SystemUIRootComponent rootComponent) {
- if (sDependency != null) {
- return;
- }
- sDependency = new Dependency();
- rootComponent.createDependency().createSystemUI(sDependency);
- sDependency.start();
- }
-
protected void start() {
// TODO: Think about ways to push these creation rules out of Dependency to cut down
// on imports.
@@ -508,6 +500,7 @@ public class Dependency {
mProviders.put(INotificationManager.class, mINotificationManager::get);
mProviders.put(FalsingManager.class, mFalsingManager::get);
mProviders.put(SysUiState.class, mSysUiStateFlagsContainer::get);
+ mProviders.put(AlarmManager.class, mAlarmManager::get);
// TODO(b/118592525): to support multi-display , we start to add something which is
// per-display, while others may be global. I think it's time to add
diff --git a/packages/SystemUI/src/com/android/systemui/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
index d46a86cc728f..239cbfe38975 100644
--- a/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
@@ -23,6 +23,7 @@ import static com.android.systemui.Dependency.MAIN_LOOPER_NAME;
import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
import android.annotation.Nullable;
+import android.app.AlarmManager;
import android.app.INotificationManager;
import android.content.Context;
import android.hardware.SensorPrivacyManager;
@@ -230,4 +231,11 @@ public class DependencyProvider {
@Named(MAIN_HANDLER_NAME) Handler mainHandler) {
return new DeviceProvisionedControllerImpl(context, mainHandler);
}
+
+ /** */
+ @Singleton
+ @Provides
+ public AlarmManager provideAlarmManager(Context context) {
+ return context.getSystemService(AlarmManager.class);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServicesDialog.java b/packages/SystemUI/src/com/android/systemui/ForegroundServicesDialog.java
index 6fec92c84fec..710980a274a2 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServicesDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServicesDialog.java
@@ -44,6 +44,8 @@ import com.android.internal.logging.nano.MetricsProto;
import java.util.ArrayList;
+import javax.inject.Inject;
+
/**
* Show a list of currently running foreground services (supplied by the caller)
* that the user can tap through to their application details.
@@ -72,10 +74,14 @@ public final class ForegroundServicesDialog extends AlertActivity implements
}
};
+ @Inject
+ ForegroundServicesDialog() {
+ super();
+ }
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- Dependency.initDependencies(SystemUIFactory.getInstance().getRootComponent());
mMetricsLogger = Dependency.get(MetricsLogger.class);
diff --git a/packages/SystemUI/src/com/android/systemui/ServiceBinder.java b/packages/SystemUI/src/com/android/systemui/ServiceBinder.java
index 131a0f8c81a9..e761a2be0b0f 100644
--- a/packages/SystemUI/src/com/android/systemui/ServiceBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/ServiceBinder.java
@@ -26,15 +26,10 @@ import dagger.multibindings.ClassKey;
import dagger.multibindings.IntoMap;
/**
- * Services and Activities that are injectable should go here.
+ * Services that are injectable should go here.
*/
@Module
public abstract class ServiceBinder {
-
- @Binds
- public abstract ContextComponentHelper bindComponentHelper(
- ContextComponentResolver componentHelper);
-
@Binds
@IntoMap
@ClassKey(DozeService.class)
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java
index e8b5285c2641..2c8324cafca0 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java
@@ -16,15 +16,15 @@
package com.android.systemui;
+import android.app.Activity;
import android.app.Application;
import android.app.Service;
import android.content.ContentProvider;
import android.content.Context;
import android.content.Intent;
-import android.os.Process;
-import android.util.Log;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.core.app.AppComponentFactory;
import javax.inject.Inject;
@@ -60,9 +60,6 @@ public class SystemUIAppComponentFactory extends AppComponentFactory {
SystemUIFactory.createFromConfig(context);
SystemUIFactory.getInstance().getRootComponent().inject(
SystemUIAppComponentFactory.this);
- Log.d(TAG, "Initialized during Application creation in Process "
- + Process.myPid() + ", Thread " + Process.myTid());
- Log.d(TAG, "mComponentHelper: " + mComponentHelper);
}
);
}
@@ -83,8 +80,6 @@ public class SystemUIAppComponentFactory extends AppComponentFactory {
SystemUIFactory.createFromConfig(context);
SystemUIFactory.getInstance().getRootComponent().inject(
contentProvider);
- Log.d(TAG, "Initialized during ContentProvider creation in Process "
- + Process.myPid() + ", Thread " + Process.myTid());
}
);
}
@@ -94,14 +89,26 @@ public class SystemUIAppComponentFactory extends AppComponentFactory {
@NonNull
@Override
+ public Activity instantiateActivityCompat(@NonNull ClassLoader cl, @NonNull String className,
+ @Nullable Intent intent)
+ throws InstantiationException, IllegalAccessException, ClassNotFoundException {
+ Activity activity = mComponentHelper.resolveActivity(className);
+ if (activity != null) {
+ return activity;
+ }
+ return super.instantiateActivityCompat(cl, className, intent);
+ }
+
+ @NonNull
+ @Override
public Service instantiateServiceCompat(
@NonNull ClassLoader cl, @NonNull String className, Intent intent)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
if (mComponentHelper == null) {
- // Everything is about to crash if this is true, but that is inevitable. We either crash
- // here or crash lower in the stack. Better to crash early!
- Log.wtf(TAG, "Uninitialized mComponentHelper in Process" + Process.myPid() + ", Thread "
- + Process.myTid());
+ // 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) {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/SystemUIBinder.java
index 88d659039b4b..4531c892a022 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIBinder.java
@@ -24,7 +24,7 @@ import dagger.multibindings.ClassKey;
import dagger.multibindings.IntoMap;
/**
- * Services and Activities that are injectable should go here.
+ * SystemUI objects that are injectable should go here.
*/
@Module
public abstract class SystemUIBinder {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index cd16d85685de..8e693185ab7f 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -102,7 +102,10 @@ public class SystemUIFactory {
// Every other part of our codebase currently relies on Dependency, so we
// really need to ensure the Dependency gets initialized early on.
- Dependency.initDependencies(mRootComponent);
+
+ Dependency dependency = new Dependency();
+ mRootComponent.createDependency().createSystemUI(dependency);
+ dependency.start();
}
protected void initWithRootComponent(@NonNull SystemUIRootComponent rootComponent) {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/SystemUIModule.java
index ff4eb83bd796..b0316e22de06 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIModule.java
@@ -24,7 +24,7 @@ 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;
-import com.android.systemui.util.AsyncSensorManager;
+import com.android.systemui.util.sensors.AsyncSensorManager;
import javax.inject.Singleton;
@@ -35,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(includes = {AssistModule.class})
+@Module(includes = {AssistModule.class, ComponentBinder.class})
public abstract class SystemUIModule {
@Singleton
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIRootComponent.java b/packages/SystemUI/src/com/android/systemui/SystemUIRootComponent.java
index 6ce673b0e9b6..c70b2fc3292a 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIRootComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIRootComponent.java
@@ -37,8 +37,6 @@ import dagger.Component;
@Component(modules = {
DependencyProvider.class,
DependencyBinder.class,
- ServiceBinder.class,
- SystemUIBinder.class,
SystemUIFactory.ContextHolder.class,
SystemUIModule.class,
SystemUIDefaultModule.class})
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIService.java b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
index 1c5e80005d84..8f1fcae8e0f7 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIService.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
@@ -84,7 +84,7 @@ public class SystemUIService extends Service {
}
} else {
String svc = args[0].toLowerCase();
- if (Dependency.class.getName().endsWith(svc)) {
+ if (Dependency.class.getName().toLowerCase().endsWith(svc)) {
Dependency.staticDump(fd, pw, args);
}
for (SystemUI ui: services) {
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleLikeHomeBehavior.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleLikeHomeBehavior.java
index a0d8b4c58f1a..ccca447ad842 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleLikeHomeBehavior.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleLikeHomeBehavior.java
@@ -23,6 +23,7 @@ import androidx.annotation.Nullable;
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;
@@ -39,44 +40,55 @@ import dagger.Lazy;
@Singleton
final class AssistHandleLikeHomeBehavior implements BehaviorController {
+ private final StatusBarStateController.StateListener mStatusBarStateListener =
+ new StatusBarStateController.StateListener() {
+ @Override
+ public void onDozingChanged(boolean isDozing) {
+ handleDozingChanged(isDozing);
+ }
+ };
private final WakefulnessLifecycle.Observer mWakefulnessLifecycleObserver =
new WakefulnessLifecycle.Observer() {
@Override
public void onStartedWakingUp() {
- handleDozingChanged(/* isDozing = */ true);
+ handleWakefullnessChanged(/* isAwake = */ false);
}
@Override
public void onFinishedWakingUp() {
- handleDozingChanged(/* isDozing = */ false);
+ handleWakefullnessChanged(/* isAwake = */ true);
}
@Override
public void onStartedGoingToSleep() {
- handleDozingChanged(/* isDozing = */ true);
+ handleWakefullnessChanged(/* isAwake = */ false);
}
@Override
public void onFinishedGoingToSleep() {
- handleDozingChanged(/* isDozing = */ true);
+ handleWakefullnessChanged(/* isAwake = */ false);
}
};
private final SysUiState.SysUiStateCallback mSysUiStateCallback =
this::handleSystemUiStateChange;
+ private final Lazy<StatusBarStateController> mStatusBarStateController;
private final Lazy<WakefulnessLifecycle> mWakefulnessLifecycle;
private final Lazy<SysUiState> mSysUiFlagContainer;
private boolean mIsDozing;
+ private boolean mIsAwake;
private boolean mIsHomeHandleHiding;
@Nullable private AssistHandleCallbacks mAssistHandleCallbacks;
@Inject
AssistHandleLikeHomeBehavior(
+ Lazy<StatusBarStateController> statusBarStateController,
Lazy<WakefulnessLifecycle> wakefulnessLifecycle,
Lazy<SysUiState> sysUiFlagContainer) {
+ mStatusBarStateController = statusBarStateController;
mWakefulnessLifecycle = wakefulnessLifecycle;
mSysUiFlagContainer = sysUiFlagContainer;
}
@@ -84,8 +96,10 @@ final class AssistHandleLikeHomeBehavior implements BehaviorController {
@Override
public void onModeActivated(Context context, AssistHandleCallbacks callbacks) {
mAssistHandleCallbacks = callbacks;
- mIsDozing = mWakefulnessLifecycle.get().getWakefulness()
- != WakefulnessLifecycle.WAKEFULNESS_AWAKE;
+ mIsDozing = mStatusBarStateController.get().isDozing();
+ mStatusBarStateController.get().addCallback(mStatusBarStateListener);
+ mIsAwake = mWakefulnessLifecycle.get().getWakefulness()
+ == WakefulnessLifecycle.WAKEFULNESS_AWAKE;
mWakefulnessLifecycle.get().addObserver(mWakefulnessLifecycleObserver);
mSysUiFlagContainer.get().addCallback(mSysUiStateCallback);
callbackForCurrentState();
@@ -94,6 +108,7 @@ final class AssistHandleLikeHomeBehavior implements BehaviorController {
@Override
public void onModeDeactivated() {
mAssistHandleCallbacks = null;
+ mStatusBarStateController.get().removeCallback(mStatusBarStateListener);
mWakefulnessLifecycle.get().removeObserver(mWakefulnessLifecycleObserver);
mSysUiFlagContainer.get().removeCallback(mSysUiStateCallback);
}
@@ -111,6 +126,15 @@ final class AssistHandleLikeHomeBehavior implements BehaviorController {
callbackForCurrentState();
}
+ private void handleWakefullnessChanged(boolean isAwake) {
+ if (mIsAwake == isAwake) {
+ return;
+ }
+
+ mIsAwake = isAwake;
+ callbackForCurrentState();
+ }
+
private void handleSystemUiStateChange(int sysuiStateFlags) {
boolean isHomeHandleHiding = isHomeHandleHiding(sysuiStateFlags);
if (mIsHomeHandleHiding == isHomeHandleHiding) {
@@ -126,18 +150,23 @@ final class AssistHandleLikeHomeBehavior implements BehaviorController {
return;
}
- if (mIsHomeHandleHiding || mIsDozing) {
+ if (mIsHomeHandleHiding || !isFullyAwake()) {
mAssistHandleCallbacks.hide();
} else {
mAssistHandleCallbacks.showAndStay();
}
}
+ private boolean isFullyAwake() {
+ return mIsAwake && !mIsDozing;
+ }
+
@Override
public void dump(PrintWriter pw, String prefix) {
- pw.println("Current AssistHandleLikeHomeBehavior State:");
+ pw.println(prefix + "Current AssistHandleLikeHomeBehavior State:");
pw.println(prefix + " mIsDozing=" + mIsDozing);
+ pw.println(prefix + " mIsAwake=" + mIsAwake);
pw.println(prefix + " mIsHomeHandleHiding=" + mIsHomeHandleHiding);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
index d371dbead42b..3ff1b97c3753 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
@@ -94,6 +94,11 @@ 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() {
@@ -119,15 +124,25 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
private final WakefulnessLifecycle.Observer mWakefulnessLifecycleObserver =
new WakefulnessLifecycle.Observer() {
@Override
+ public void onStartedWakingUp() {
+ handleWakefullnessChanged(/* isAwake = */ false);
+ }
+
+ @Override
public void onFinishedWakingUp() {
- handleDozingChanged(false);
+ handleWakefullnessChanged(/* isAwake = */ true);
}
@Override
public void onStartedGoingToSleep() {
- handleDozingChanged(true);
+ handleWakefullnessChanged(/* isAwake = */ false);
}
- };
+
+ @Override
+ public void onFinishedGoingToSleep() {
+ handleWakefullnessChanged(/* isAwake = */ false);
+ }
+ };
private final BroadcastReceiver mDefaultHomeBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -149,6 +164,7 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
private boolean mOnLockscreen;
private boolean mIsDozing;
+ private boolean mIsAwake;
private int mRunningTaskId;
private boolean mIsNavBarHidden;
private boolean mIsLauncherShowing;
@@ -201,6 +217,7 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
mDefaultHome = getCurrentDefaultHome();
context.registerReceiver(mDefaultHomeBroadcastReceiver, mDefaultHomeIntentFilter);
mOnLockscreen = onLockscreen(mStatusBarStateController.get().getState());
+ mIsDozing = mStatusBarStateController.get().isDozing();
mStatusBarStateController.get().addCallback(mStatusBarStateListener);
ActivityManager.RunningTaskInfo runningTaskInfo =
mActivityManagerWrapper.get().getRunningTask();
@@ -208,8 +225,8 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
mActivityManagerWrapper.get().registerTaskStackListener(mTaskStackChangeListener);
mOverviewProxyService.get().addCallback(mOverviewProxyListener);
mSysUiFlagContainer.get().addCallback(mSysUiStateCallback);
- mIsDozing = mWakefulnessLifecycle.get().getWakefulness()
- != WakefulnessLifecycle.WAKEFULNESS_AWAKE;
+ mIsAwake = mWakefulnessLifecycle.get().getWakefulness()
+ == WakefulnessLifecycle.WAKEFULNESS_AWAKE;
mWakefulnessLifecycle.get().addObserver(mWakefulnessLifecycleObserver);
mLearningTimeElapsed = Settings.Secure.getLong(
@@ -252,7 +269,10 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
@Override
public void onAssistHandlesRequested() {
- if (mAssistHandleCallbacks != null && !mIsDozing && !mIsNavBarHidden && !mOnLockscreen) {
+ if (mAssistHandleCallbacks != null
+ && isFullyAwake()
+ && !mIsNavBarHidden
+ && !mOnLockscreen) {
mAssistHandleCallbacks.showAndGo();
}
}
@@ -299,6 +319,16 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
callbackForCurrentState(/* justUnlocked = */ false);
}
+ private void handleWakefullnessChanged(boolean isAwake) {
+ if (mIsAwake == isAwake) {
+ return;
+ }
+
+ resetConsecutiveTaskSwitches();
+ mIsAwake = isAwake;
+ callbackForCurrentState(/* justUnlocked = */ false);
+ }
+
private void handleTaskStackTopChanged(int taskId, @Nullable ComponentName taskComponentName) {
if (mRunningTaskId == taskId || taskComponentName == null) {
return;
@@ -352,7 +382,7 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
return;
}
- if (mIsDozing || mIsNavBarHidden || mOnLockscreen || !getShowWhenTaught()) {
+ if (!isFullyAwake() || mIsNavBarHidden || mOnLockscreen || !getShowWhenTaught()) {
mAssistHandleCallbacks.hide();
} else if (justUnlocked) {
long currentEpochDay = LocalDate.now().toEpochDay();
@@ -374,7 +404,7 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
return;
}
- if (mIsDozing || mIsNavBarHidden || isSuppressed()) {
+ if (!isFullyAwake() || mIsNavBarHidden || isSuppressed()) {
mAssistHandleCallbacks.hide();
} else if (mOnLockscreen) {
mAssistHandleCallbacks.showAndStay();
@@ -425,6 +455,10 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
mHandler.postDelayed(mResetConsecutiveTaskSwitches, getShowAndGoDelayResetTimeoutMs());
}
+ private boolean isFullyAwake() {
+ return mIsAwake && !mIsDozing;
+ }
+
private long getLearningTimeMs() {
return mPhenotypeHelper.getLong(
SystemUiDeviceConfigFlags.ASSIST_HANDLES_LEARN_TIME_MS,
@@ -484,6 +518,7 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
pw.println(prefix + "Current AssistHandleReminderExpBehavior State:");
pw.println(prefix + " mOnLockscreen=" + mOnLockscreen);
pw.println(prefix + " mIsDozing=" + mIsDozing);
+ pw.println(prefix + " mIsAwake=" + mIsAwake);
pw.println(prefix + " mRunningTaskId=" + mRunningTaskId);
pw.println(prefix + " mDefaultHome=" + mDefaultHome);
pw.println(prefix + " mIsNavBarHidden=" + mIsNavBarHidden);
diff --git a/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java b/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java
index 0c4f05123c79..b346a6eb2b3c 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java
@@ -17,6 +17,7 @@
package com.android.systemui.assist.ui;
import static com.android.systemui.assist.AssistManager.DISMISS_REASON_INVOCATION_CANCELLED;
+import static com.android.systemui.assist.AssistManager.INVOCATION_TYPE_GESTURE;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -24,6 +25,7 @@ import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.PixelFormat;
import android.metrics.LogMaker;
+import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.Gravity;
@@ -40,6 +42,8 @@ import com.android.systemui.ScreenDecorations;
import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.assist.AssistManager;
+import java.util.Locale;
+
/**
* Default UiController implementation. Shows white edge lights along the bottom of the phone,
* expanding from the corners to meet in the center.
@@ -50,6 +54,9 @@ public class DefaultUiController implements AssistManager.UiController {
private static final long ANIM_DURATION_MS = 200;
+ private static final boolean VERBOSE = Build.TYPE.toLowerCase(Locale.ROOT).contains("debug")
+ || Build.TYPE.toLowerCase(Locale.ROOT).equals("eng");
+
protected final FrameLayout mRoot;
protected InvocationLightsView mInvocationLightsView;
@@ -114,6 +121,7 @@ public class DefaultUiController implements AssistManager.UiController {
@Override // AssistManager.UiController
public void onGestureCompletion(float velocity) {
animateInvocationCompletion(AssistManager.INVOCATION_TYPE_GESTURE, velocity);
+ logInvocationProgressMetrics(INVOCATION_TYPE_GESTURE, 1, mInvocationInProgress);
}
@Override // AssistManager.UiController
@@ -127,16 +135,27 @@ public class DefaultUiController implements AssistManager.UiController {
updateAssistHandleVisibility();
}
- protected static void logInvocationProgressMetrics(
+ protected void logInvocationProgressMetrics(
int type, float progress, boolean invocationWasInProgress) {
// Logs assistant invocation start.
+ if (progress == 1f) {
+ if (VERBOSE) {
+ Log.v(TAG, "Invocation complete: type=" + type);
+ }
+ }
if (!invocationWasInProgress && progress > 0.f) {
+ if (VERBOSE) {
+ Log.v(TAG, "Invocation started: type=" + type);
+ }
MetricsLogger.action(new LogMaker(MetricsEvent.ASSISTANT)
.setType(MetricsEvent.TYPE_ACTION)
.setSubtype(Dependency.get(AssistManager.class).toLoggingSubType(type)));
}
// Logs assistant invocation cancelled.
- if (invocationWasInProgress && progress == 0f) {
+ if (!mInvocationAnimator.isRunning() && invocationWasInProgress && progress == 0f) {
+ if (VERBOSE) {
+ Log.v(TAG, "Invocation cancelled: type=" + type);
+ }
MetricsLogger.action(new LogMaker(MetricsEvent.ASSISTANT)
.setType(MetricsEvent.TYPE_DISMISS)
.setSubtype(DISMISS_REASON_INVOCATION_CANCELLED));
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
index e4d2005c8867..73bbce9c5b35 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
@@ -608,7 +608,10 @@ public abstract class AuthBiometricView extends LinearLayout {
MeasureSpec.makeMeasureSpec(newWidth, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
}
- totalHeight += child.getMeasuredHeight();
+
+ if (child.getVisibility() != View.GONE) {
+ totalHeight += child.getMeasuredHeight();
+ }
}
// Use the new width so it's centered horizontally
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 7c6792ce5d48..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 DISABLE_NEW_DIALOG =
- "com.android.systemui.biometrics.AuthController.DISABLE_NEW_DIALOG";
private static final String TAG = "BiometricPrompt/AuthController";
private static final boolean DEBUG = true;
@@ -316,25 +313,13 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
protected AuthDialog buildDialog(Bundle biometricPromptBundle, boolean requireConfirmation,
int userId, int type, String opPackageName, boolean skipIntro) {
- if (Settings.Secure.getIntForUser(
- mContext.getContentResolver(), DISABLE_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/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/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 67fc3e32883f..72ada6e90cd0 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -48,12 +48,10 @@ import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.pm.ActivityInfo;
-import android.content.pm.ParceledListSlice;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.provider.Settings;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.ZenModeConfig;
import android.util.ArraySet;
@@ -61,8 +59,6 @@ import android.util.Log;
import android.util.Pair;
import android.util.SparseSetArray;
import android.view.Display;
-import android.view.IPinnedStackController;
-import android.view.IPinnedStackListener;
import android.view.ViewGroup;
import android.widget.FrameLayout;
@@ -76,6 +72,7 @@ import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.PinnedStackListenerForwarder;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.WindowManagerWrapper;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -131,9 +128,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
public static final int MAX_BUBBLES = 5; // TODO: actually enforce this
- /** Flag to enable or disable the entire feature */
- private static final String ENABLE_BUBBLES = "experiment_enable_bubbles";
-
private final Context mContext;
private final NotificationEntryManager mNotificationEntryManager;
private final BubbleTaskStackListener mTaskStackListener;
@@ -560,7 +554,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
// Need to check for !appCancel here because the notification may have
// previously been dismissed & entry.isRowDismissed would still be true
- boolean userRemovedNotif = (entry.isRowDismissed() && !isAppCancel)
+ boolean userRemovedNotif = (entry != null && entry.isRowDismissed() && !isAppCancel)
|| isClearAll || isUserDimiss || isSummaryCancel;
if (isSummaryOfBubbles) {
@@ -570,7 +564,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
// The bubble notification sticks around in the data as long as the bubble is
// not dismissed and the app hasn't cancelled the notification.
Bubble bubble = mBubbleData.getBubbleWithKey(key);
- boolean bubbleExtended = entry.isBubble() && userRemovedNotif;
+ boolean bubbleExtended = entry != null && entry.isBubble() && userRemovedNotif;
if (bubbleExtended) {
bubble.setShowInShadeWhenBubble(false);
bubble.setShowBubbleDot(false);
@@ -579,7 +573,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
}
mNotificationEntryManager.updateNotifications();
return true;
- } else if (!userRemovedNotif) {
+ } else if (!userRemovedNotif && entry != null) {
// This wasn't a user removal so we should remove the bubble as well
mBubbleData.notificationEntryRemoved(entry, DISMISS_NOTIF_CANCEL);
return false;
@@ -638,9 +632,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
private final NotificationEntryListener mEntryListener = new NotificationEntryListener() {
@Override
public void onPendingEntryAdded(NotificationEntry entry) {
- if (!areBubblesEnabled(mContext)) {
- return;
- }
if (mNotificationInterruptionStateProvider.shouldBubbleUp(entry)
&& canLaunchInActivityView(mContext, entry)) {
updateBubble(entry);
@@ -649,9 +640,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
@Override
public void onPreEntryUpdated(NotificationEntry entry) {
- if (!areBubblesEnabled(mContext)) {
- return;
- }
boolean shouldBubble = mNotificationInterruptionStateProvider.shouldBubbleUp(entry)
&& canLaunchInActivityView(mContext, entry);
if (!shouldBubble && mBubbleData.hasBubbleWithKey(entry.key)) {
@@ -926,7 +914,9 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
@Override
public void onSingleTaskDisplayDrawn(int displayId) {
- final Bubble expandedBubble = getExpandedBubble(mContext);
+ final Bubble expandedBubble = mStackView != null
+ ? mStackView.getExpandedBubble()
+ : null;
if (expandedBubble != null && expandedBubble.getDisplayId() == displayId) {
expandedBubble.setContentVisibility(true);
}
@@ -934,22 +924,17 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
@Override
public void onSingleTaskDisplayEmpty(int displayId) {
- final Bubble expandedBubble = getExpandedBubble(mContext);
- if (expandedBubble == null) {
- return;
- }
- if (expandedBubble.getDisplayId() == displayId) {
+ final Bubble expandedBubble = mStackView != null
+ ? mStackView.getExpandedBubble()
+ : null;
+ int expandedId = expandedBubble != null ? expandedBubble.getDisplayId() : -1;
+ if (mStackView != null && mStackView.isExpanded() && expandedId == displayId) {
mBubbleData.setExpanded(false);
- expandedBubble.getExpandedView().notifyDisplayEmpty();
}
+ mBubbleData.notifyDisplayEmpty(displayId);
}
}
- private static boolean areBubblesEnabled(Context context) {
- return Settings.Secure.getInt(context.getContentResolver(),
- ENABLE_BUBBLES, 1) != 0;
- }
-
/**
* Whether an intent is properly configured to display in an {@link android.app.ActivityView}.
*
@@ -993,32 +978,12 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
}
/** PinnedStackListener that dispatches IME visibility updates to the stack. */
- private class BubblesImeListener extends IPinnedStackListener.Stub {
-
- @Override
- public void onListenerRegistered(IPinnedStackController controller) throws RemoteException {
- }
-
- @Override
- public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds,
- Rect animatingBounds, boolean fromImeAdjustment, boolean fromShelfAdjustment,
- int displayRotation) throws RemoteException {}
-
+ private class BubblesImeListener extends PinnedStackListenerForwarder.PinnedStackListener {
@Override
public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
if (mStackView != null && mStackView.getBubbleCount() > 0) {
mStackView.post(() -> mStackView.onImeVisibilityChanged(imeVisible, imeHeight));
}
}
-
- @Override
- public void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight)
- throws RemoteException {}
-
- @Override
- public void onMinimizedStateChanged(boolean isMinimized) throws RemoteException {}
-
- @Override
- public void onActionsChanged(ParceledListSlice actions) throws RemoteException {}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index 81c8da8247ec..d43e030ed9eb 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -184,6 +184,8 @@ public class BubbleData {
Log.d(TAG, "notificationEntryUpdated: " + entry);
}
Bubble bubble = getBubbleWithKey(entry.key);
+ suppressFlyout = !entry.isVisuallyInterruptive || suppressFlyout;
+
if (bubble == null) {
// Create a new bubble
bubble = new Bubble(mContext, entry);
@@ -193,8 +195,10 @@ public class BubbleData {
} else {
// Updates an existing bubble
bubble.updateEntry(entry);
+ bubble.setSuppressFlyout(suppressFlyout);
doUpdate(bubble);
}
+
if (bubble.shouldAutoExpand()) {
setSelectedBubbleInternal(bubble);
if (!mExpanded) {
@@ -392,6 +396,22 @@ public class BubbleData {
dispatchPendingChanges();
}
+ /**
+ * Indicates that the provided display is no longer in use and should be cleaned up.
+ *
+ * @param displayId the id of the display to clean up.
+ */
+ void notifyDisplayEmpty(int displayId) {
+ for (Bubble b : mBubbles) {
+ if (b.getDisplayId() == displayId) {
+ if (b.getExpandedView() != null) {
+ b.getExpandedView().notifyDisplayEmpty();
+ }
+ return;
+ }
+ }
+ }
+
private void dispatchPendingChanges() {
if (mListener != null && mStateChange.anythingChanged()) {
mListener.applyUpdate(mStateChange);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index be10dc565159..521ebde7d2f0 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -105,6 +105,7 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
private Drawable mAppIcon;
private BubbleController mBubbleController = Dependency.get(BubbleController.class);
+ private WindowManager mWindowManager;
private BubbleStackView mStackView;
@@ -202,9 +203,9 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
super(context, attrs, defStyleAttr, defStyleRes);
mPm = context.getPackageManager();
mDisplaySize = new Point();
- WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
// Get the real size -- this includes screen decorations (notches, statusbar, navbar).
- wm.getDefaultDisplay().getRealSize(mDisplaySize);
+ mWindowManager.getDefaultDisplay().getRealSize(mDisplaySize);
Resources res = getResources();
mMinHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height);
mPointerMargin = res.getDimensionPixelSize(R.dimen.bubble_pointer_margin);
@@ -326,7 +327,10 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
if (usingActivityView()) {
int[] screenLoc = mActivityView.getLocationOnScreen();
final int activityViewBottom = screenLoc[1] + mActivityView.getHeight();
- final int keyboardTop = mDisplaySize.y - insets.getSystemWindowInsetBottom();
+ final int keyboardTop = mDisplaySize.y - Math.max(insets.getSystemWindowInsetBottom(),
+ insets.getDisplayCutout() != null
+ ? insets.getDisplayCutout().getSafeInsetBottom()
+ : 0);
final int insetsBottom = Math.max(activityViewBottom - keyboardTop, 0);
mActivityView.setForwardedInsets(Insets.of(0, 0, 0, insetsBottom));
}
@@ -444,6 +448,7 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
}
private int getMaxExpandedHeight() {
+ mWindowManager.getDefaultDisplay().getRealSize(mDisplaySize);
int[] windowLocation = mActivityView.getLocationOnScreen();
int bottomInset = getRootWindowInsets() != null
? getRootWindowInsets().getStableInsetBottom()
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index cc250b46c040..31cf853dce04 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -45,6 +45,7 @@ import android.service.notification.StatusBarNotification;
import android.util.Log;
import android.util.StatsLog;
import android.view.Choreographer;
+import android.view.DisplayCutout;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -340,7 +341,8 @@ public class BubbleStackView extends FrameLayout {
mDisplaySize = new Point();
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
- wm.getDefaultDisplay().getSize(mDisplaySize);
+ // We use the real size & subtract screen decorations / window insets ourselves when needed
+ wm.getDefaultDisplay().getRealSize(mDisplaySize);
mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
@@ -417,7 +419,35 @@ public class BubbleStackView extends FrameLayout {
mOrientationChangedListener =
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
- mExpandedAnimationController.updateOrientation(mOrientation);
+ mExpandedAnimationController.updateOrientation(mOrientation, mDisplaySize);
+ mStackAnimationController.updateOrientation(mOrientation);
+
+ // Reposition & adjust the height for new orientation
+ if (mIsExpanded) {
+ mExpandedViewContainer.setTranslationY(getExpandedViewY());
+ mExpandedBubble.getExpandedView().updateView();
+ }
+
+ // Need to update the padding around the view
+ WindowInsets insets = getRootWindowInsets();
+ int leftPadding = mExpandedViewPadding;
+ int rightPadding = mExpandedViewPadding;
+ if (insets != null) {
+ // Can't have the expanded view overlaying notches
+ int cutoutLeft = 0;
+ int cutoutRight = 0;
+ DisplayCutout cutout = insets.getDisplayCutout();
+ if (cutout != null) {
+ cutoutLeft = cutout.getSafeInsetLeft();
+ cutoutRight = cutout.getSafeInsetRight();
+ }
+ // Or overlaying nav or status bar
+ leftPadding += Math.max(cutoutLeft, insets.getStableInsetLeft());
+ rightPadding += Math.max(cutoutRight, insets.getStableInsetRight());
+ }
+ mExpandedViewContainer.setPadding(leftPadding, mExpandedViewPadding,
+ rightPadding, mExpandedViewPadding);
+
if (mIsExpanded) {
// Re-draw bubble row and pointer for new orientation.
mExpandedAnimationController.expandFromStack(() -> {
@@ -488,6 +518,11 @@ public class BubbleStackView extends FrameLayout {
public void onOrientationChanged(int orientation) {
mOrientation = orientation;
+ // Display size is based on the rotation device was in when requested, we should update it
+ // We use the real size & subtract screen decorations / window insets ourselves when needed
+ WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
+ wm.getDefaultDisplay().getRealSize(mDisplaySize);
+
// Some resources change depending on orientation
Resources res = getContext().getResources();
mStatusBarHeight = res.getDimensionPixelSize(
@@ -1584,11 +1619,9 @@ public class BubbleStackView extends FrameLayout {
int index = getBubbleIndex(expandedBubble);
float bubbleLeftFromScreenLeft = mExpandedAnimationController.getBubbleLeft(index);
float halfBubble = mBubbleSize / 2f;
-
- // Bubbles live in expanded view container (x includes expanded view padding).
- // Pointer lives in expanded view, which has padding (x does not include padding).
- // Remove padding when deriving pointer location from bubbles.
- float bubbleCenter = bubbleLeftFromScreenLeft + halfBubble - mExpandedViewPadding;
+ float bubbleCenter = bubbleLeftFromScreenLeft + halfBubble;
+ // Padding might be adjusted for insets, so get it directly from the view
+ bubbleCenter -= mExpandedViewContainer.getPaddingLeft();
expandedBubble.getExpandedView().setPointerPosition(bubbleCenter);
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
index c332d15a9b47..59d68bca93c3 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
@@ -21,6 +21,7 @@ import android.content.res.Resources;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.PointF;
+import android.view.DisplayCutout;
import android.view.View;
import android.view.WindowInsets;
@@ -57,6 +58,9 @@ public class ExpandedAnimationController
/** Stiffness for the expand/collapse path-following animation. */
private static final int EXPAND_COLLAPSE_ANIM_STIFFNESS = 1000;
+ /** What percentage of the screen to use when centering the bubbles in landscape. */
+ private static final float CENTER_BUBBLES_LANDSCAPE_PERCENT = 0.66f;
+
/** Horizontal offset between bubbles, which we need to know to re-stack them. */
private float mStackOffsetPx;
/** Space between status bar and bubbles in the expanded state. */
@@ -69,8 +73,8 @@ public class ExpandedAnimationController
private Point mDisplaySize;
/** Max number of bubbles shown in row above expanded view.*/
private int mBubblesMaxRendered;
- /** Width of current screen orientation. */
- private float mScreenWidth;
+ /** What the current screen orientation is. */
+ private int mScreenOrientation;
/** Whether the dragged-out bubble is in the dismiss target. */
private boolean mIndividualBubbleWithinDismissTarget = false;
@@ -97,8 +101,7 @@ public class ExpandedAnimationController
public ExpandedAnimationController(Point displaySize, int expandedViewPadding,
int orientation) {
- mDisplaySize = displaySize;
- updateOrientation(orientation);
+ updateOrientation(orientation, displaySize);
mExpandedViewPadding = expandedViewPadding;
mLauncherGridDiff = 30f;
}
@@ -136,18 +139,16 @@ public class ExpandedAnimationController
/**
* Update effective screen width based on current orientation.
* @param orientation Landscape or portrait.
+ * @param displaySize Updated display size.
*/
- public void updateOrientation(int orientation) {
- if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
- mScreenWidth = mDisplaySize.y;
- } else {
- mScreenWidth = mDisplaySize.x;
- }
+ public void updateOrientation(int orientation, Point displaySize) {
+ mScreenOrientation = orientation;
+ mDisplaySize = displaySize;
if (mLayout != null) {
Resources res = mLayout.getContext().getResources();
+ mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
mStatusBarHeight = res.getDimensionPixelSize(
com.android.internal.R.dimen.status_bar_height);
- mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
}
}
@@ -499,6 +500,50 @@ public class ExpandedAnimationController
return getRowLeft() + bubbleFromRowLeft;
}
+ /**
+ * When expanded, the bubbles are centered in the screen. In portrait, all available space is
+ * used. In landscape we have too much space so the value is restricted. This method accounts
+ * for window decorations (nav bar, cutouts).
+ *
+ * @return the desired width to display the expanded bubbles in.
+ */
+ private float getWidthForDisplayingBubbles() {
+ final float availableWidth = getAvailableScreenWidth(true /* includeStableInsets */);
+ if (mScreenOrientation == Configuration.ORIENTATION_LANDSCAPE) {
+ // display size y in landscape will be the smaller dimension of the screen
+ return Math.max(mDisplaySize.y, availableWidth * CENTER_BUBBLES_LANDSCAPE_PERCENT);
+ } else {
+ return availableWidth;
+ }
+ }
+
+ /**
+ * Determines the available screen width without the cutout.
+ *
+ * @param subtractStableInsets Whether or not stable insets should also be removed from the
+ * returned width.
+ * @return the total screen width available accounting for cutouts and insets,
+ * iff {@param includeStableInsets} is true.
+ */
+ private float getAvailableScreenWidth(boolean subtractStableInsets) {
+ float availableSize = mDisplaySize.x;
+ WindowInsets insets = mLayout != null ? mLayout.getRootWindowInsets() : null;
+ if (insets != null) {
+ int cutoutLeft = 0;
+ int cutoutRight = 0;
+ DisplayCutout cutout = insets.getDisplayCutout();
+ if (cutout != null) {
+ cutoutLeft = cutout.getSafeInsetLeft();
+ cutoutRight = cutout.getSafeInsetRight();
+ }
+ final int stableLeft = subtractStableInsets ? insets.getStableInsetLeft() : 0;
+ final int stableRight = subtractStableInsets ? insets.getStableInsetRight() : 0;
+ availableSize -= Math.max(stableLeft, cutoutLeft);
+ availableSize -= Math.max(stableRight, cutoutRight);
+ }
+ return availableSize;
+ }
+
private float getRowLeft() {
if (mLayout == null) {
return 0;
@@ -510,9 +555,12 @@ public class ExpandedAnimationController
final float totalGapWidth = (bubbleCount - 1) * getSpaceBetweenBubbles();
final float rowWidth = totalGapWidth + totalBubbleWidth;
- final float centerScreen = mScreenWidth / 2f;
+ // This display size we're using includes the size of the insets, we want the true
+ // center of the display minus the notch here, which means we should include the
+ // stable insets (e.g. status bar, nav bar) in this calculation.
+ final float trueCenter = getAvailableScreenWidth(false /* subtractStableInsets */) / 2f;
final float halfRow = rowWidth / 2f;
- final float rowLeft = centerScreen - halfRow;
+ final float rowLeft = trueCenter - halfRow;
return rowLeft;
}
@@ -530,7 +578,7 @@ public class ExpandedAnimationController
* Launcher's app icon grid edge that we must match
*/
final float rowMargins = (mExpandedViewPadding + mLauncherGridDiff) * 2;
- final float maxRowWidth = mScreenWidth - rowMargins;
+ final float maxRowWidth = getWidthForDisplayingBubbles() - rowMargins;
final float totalBubbleWidth = mBubblesMaxRendered * mBubbleSizePx;
final float totalGapWidth = maxRowWidth - totalBubbleWidth;
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
index c2b0fe4ab2d9..20742d6d2358 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
@@ -43,7 +43,7 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.util.AsyncSensorManager;
+import com.android.systemui.util.sensors.AsyncSensorManager;
import java.io.PrintWriter;
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
index c9f5b79918f8..914258f48b46 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
@@ -20,6 +20,7 @@ import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHT
import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
import android.content.Context;
+import android.hardware.SensorManager;
import android.net.Uri;
import android.os.Handler;
import android.provider.DeviceConfig;
@@ -34,8 +35,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.sensors.ProximitySensor;
import java.io.PrintWriter;
@@ -51,6 +52,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 +62,12 @@ 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);
+ mProximitySensor.setSensorDelay(SensorManager.SENSOR_DELAY_GAME);
mDeviceConfig = deviceConfig;
mDeviceConfigListener =
properties -> onDeviceConfigPropertiesChanged(context, properties.getNamespace());
@@ -113,8 +122,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..bd0906a63370 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,12 @@ 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.sensors.ProximitySensor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
+import java.util.Locale;
/**
* FalsingManager designed to make clear why a touch was rejected.
@@ -47,11 +43,11 @@ import java.util.concurrent.Executors;
public class BrightLineFalsingManager implements FalsingManager {
static final boolean DEBUG = false;
- private static final String TAG = "FalsingManagerPlugin";
+ private static final String TAG = "FalsingManager";
- 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 +55,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 +73,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 +96,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() {
@@ -170,7 +143,15 @@ public class BrightLineFalsingManager implements FalsingManager {
boolean r = !mJustUnlockedWithFace && mClassifiers.stream().anyMatch(falsingClassifier -> {
boolean result = falsingClassifier.isFalseTouch();
if (result) {
- logInfo(falsingClassifier.getClass().getName() + ": true");
+ logInfo(String.format(
+ (Locale) null,
+ "{classifier=%s, interactionType=%d}",
+ falsingClassifier.getClass().getName(),
+ mDataProvider.getInteractionType()));
+ String reason = falsingClassifier.getReason();
+ if (reason != null) {
+ logInfo(reason);
+ }
} else {
logDebug(falsingClassifier.getClass().getName() + ": false");
}
@@ -190,10 +171,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/DiagonalClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/DiagonalClassifier.java
index 9c03b915000e..520e0a12dec4 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DiagonalClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/DiagonalClassifier.java
@@ -25,6 +25,8 @@ import android.provider.DeviceConfig;
import com.android.systemui.util.DeviceConfigProxy;
+import java.util.Locale;
+
/**
* False on swipes that are too close to 45 degrees.
*
@@ -84,6 +86,15 @@ class DiagonalClassifier extends FalsingClassifier {
maxAngle + ONE_HUNDRED_EIGHTY_DEG);
}
+ @Override
+ String getReason() {
+ return String.format(
+ (Locale) null,
+ "{angle=%f, vertical=%s}",
+ getAngle(),
+ isVertical());
+ }
+
private boolean angleBetween(float angle, float min, float max) {
// No need to normalize angle as it is guaranteed to be between 0 and 2*PI.
min = normalizeAngle(min);
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DistanceClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/DistanceClassifier.java
index 0954f9e6e672..7f45cc8f4c56 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DistanceClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/DistanceClassifier.java
@@ -30,6 +30,7 @@ import android.view.VelocityTracker;
import com.android.systemui.util.DeviceConfigProxy;
import java.util.List;
+import java.util.Locale;
/**
* Ensure that the swipe + momentum covers a minimum distance.
@@ -144,15 +145,66 @@ class DistanceClassifier extends FalsingClassifier {
@Override
public boolean isFalseTouch() {
- return !getDistances().getPassedFlingThreshold();
+ return !getPassedFlingThreshold();
+ }
+
+ @Override
+ String getReason() {
+ DistanceVectors distanceVectors = getDistances();
+
+ return String.format(
+ (Locale) null,
+ "{distanceVectors=%s, isHorizontal=%s, velocityToDistanceMultiplier=%f, "
+ + "horizontalFlingThreshold=%f, verticalFlingThreshold=%f, "
+ + "horizontalSwipeThreshold=%f, verticalSwipeThreshold=%s}",
+ distanceVectors,
+ isHorizontal(),
+ mVelocityToDistanceMultiplier,
+ mHorizontalFlingThresholdPx,
+ mVerticalFlingThresholdPx,
+ mHorizontalSwipeThresholdPx,
+ mVerticalSwipeThresholdPx);
}
boolean isLongSwipe() {
- boolean longSwipe = getDistances().getPassedDistanceThreshold();
+ boolean longSwipe = getPassedDistanceThreshold();
logDebug("Is longSwipe? " + longSwipe);
return longSwipe;
}
+ private boolean getPassedDistanceThreshold() {
+ DistanceVectors distanceVectors = getDistances();
+ if (isHorizontal()) {
+ logDebug("Horizontal swipe distance: " + Math.abs(distanceVectors.mDx));
+ logDebug("Threshold: " + mHorizontalSwipeThresholdPx);
+
+ return Math.abs(distanceVectors.mDx) >= mHorizontalSwipeThresholdPx;
+ }
+
+ logDebug("Vertical swipe distance: " + Math.abs(distanceVectors.mDy));
+ logDebug("Threshold: " + mVerticalSwipeThresholdPx);
+ return Math.abs(distanceVectors.mDy) >= mVerticalSwipeThresholdPx;
+ }
+
+ private boolean getPassedFlingThreshold() {
+ DistanceVectors distanceVectors = getDistances();
+
+ float dX = distanceVectors.mDx + distanceVectors.mVx * mVelocityToDistanceMultiplier;
+ float dY = distanceVectors.mDy + distanceVectors.mVy * mVelocityToDistanceMultiplier;
+
+ if (isHorizontal()) {
+ logDebug("Horizontal swipe and fling distance: " + distanceVectors.mDx + ", "
+ + distanceVectors.mVx * mVelocityToDistanceMultiplier);
+ logDebug("Threshold: " + mHorizontalFlingThresholdPx);
+ return Math.abs(dX) >= mHorizontalFlingThresholdPx;
+ }
+
+ logDebug("Vertical swipe and fling distance: " + distanceVectors.mDy + ", "
+ + distanceVectors.mVy * mVelocityToDistanceMultiplier);
+ logDebug("Threshold: " + mVerticalFlingThresholdPx);
+ return Math.abs(dY) >= mVerticalFlingThresholdPx;
+ }
+
private class DistanceVectors {
final float mDx;
final float mDy;
@@ -166,34 +218,9 @@ class DistanceClassifier extends FalsingClassifier {
this.mVy = vY;
}
- boolean getPassedDistanceThreshold() {
- if (isHorizontal()) {
- logDebug("Horizontal swipe distance: " + Math.abs(mDx));
- logDebug("Threshold: " + mHorizontalSwipeThresholdPx);
-
- return Math.abs(mDx) >= mHorizontalSwipeThresholdPx;
- }
-
- logDebug("Vertical swipe distance: " + Math.abs(mDy));
- logDebug("Threshold: " + mVerticalSwipeThresholdPx);
- return Math.abs(mDy) >= mVerticalSwipeThresholdPx;
- }
-
- boolean getPassedFlingThreshold() {
- float dX = this.mDx + this.mVx * mVelocityToDistanceMultiplier;
- float dY = this.mDy + this.mVy * mVelocityToDistanceMultiplier;
-
- if (isHorizontal()) {
- logDebug("Horizontal swipe and fling distance: " + this.mDx + ", "
- + this.mVx * mVelocityToDistanceMultiplier);
- logDebug("Threshold: " + mHorizontalFlingThresholdPx);
- return Math.abs(dX) >= mHorizontalFlingThresholdPx;
- }
-
- logDebug("Vertical swipe and fling distance: " + this.mDy + ", "
- + this.mVy * mVelocityToDistanceMultiplier);
- logDebug("Threshold: " + mVerticalFlingThresholdPx);
- return Math.abs(dY) >= mVerticalFlingThresholdPx;
+ @Override
+ public String toString() {
+ return String.format((Locale) null, "{dx=%f, vx=%f, dy=%f, vy=%f}", mDx, mVx, mDy, mVy);
}
}
}
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..7555051fac48 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.sensors.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.
@@ -117,6 +117,14 @@ abstract class FalsingClassifier {
*/
abstract boolean isFalseTouch();
+ /**
+ * Give the classifier a chance to log more details about why it triggered.
+ *
+ * This should only be called after a call to {@link #isFalseTouch()}, and only if
+ * {@link #isFalseTouch()} returns true;
+ */
+ abstract String getReason();
+
static void logDebug(String msg) {
BrightLineFalsingManager.logDebug(msg);
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/PointerCountClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/PointerCountClassifier.java
index 40e141fbf988..85a4d234b5df 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/PointerCountClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/PointerCountClassifier.java
@@ -18,6 +18,8 @@ package com.android.systemui.classifier.brightline;
import android.view.MotionEvent;
+import java.util.Locale;
+
/**
* False touch if more than one finger touches the screen.
*
@@ -50,4 +52,13 @@ class PointerCountClassifier extends FalsingClassifier {
public boolean isFalseTouch() {
return mMaxPointerCount > MAX_ALLOWED_POINTERS;
}
+
+ @Override
+ String getReason() {
+ return String.format(
+ (Locale) null,
+ "{pointersObserved=%d, threshold=%d}",
+ mMaxPointerCount,
+ MAX_ALLOWED_POINTERS);
+ }
}
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..749914e1b625 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,13 @@ 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.sensors.ProximitySensor;
+
+import java.util.Locale;
/**
@@ -99,14 +100,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
@@ -124,6 +123,16 @@ class ProximityClassifier extends FalsingClassifier {
return false;
}
+ @Override
+ String getReason() {
+ return String.format(
+ (Locale) null,
+ "{percentInProximity=%f, threshold=%f, distanceClassifier=%s}",
+ mPercentNear,
+ mPercentCoveredThreshold,
+ mDistanceClassifier.getReason());
+ }
+
/**
* @param near is the sensor showing the near state right now
* @param timeStampNs time of this event in nanoseconds
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/TypeClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/TypeClassifier.java
index b6ceab559301..5f1b37ae06d9 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/TypeClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/TypeClassifier.java
@@ -58,4 +58,9 @@ public class TypeClassifier extends FalsingClassifier {
return true;
}
}
+
+ @Override
+ String getReason() {
+ return String.format("{vertical=%s, up=%s, right=%s}", isVertical(), isUp(), isRight());
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java
index a0da988b27b5..957ea8d127d2 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java
@@ -29,6 +29,7 @@ import com.android.systemui.util.DeviceConfigProxy;
import java.util.ArrayList;
import java.util.List;
+import java.util.Locale;
/**
* Penalizes gestures that change direction in either the x or y too much.
@@ -49,6 +50,10 @@ class ZigZagClassifier extends FalsingClassifier {
private final float mMaxYPrimaryDeviance;
private final float mMaxXSecondaryDeviance;
private final float mMaxYSecondaryDeviance;
+ private float mLastDevianceX;
+ private float mLastDevianceY;
+ private float mLastMaxXDeviance;
+ private float mLastMaxYDeviance;
ZigZagClassifier(FalsingDataProvider dataProvider, DeviceConfigProxy deviceConfigProxy) {
super(dataProvider);
@@ -139,11 +144,25 @@ class ZigZagClassifier extends FalsingClassifier {
maxYDeviance = mMaxYPrimaryDeviance * totalDistanceIn * getYdpi();
}
+ // These values are saved for logging reasons. {@see #getReason()}
+ mLastDevianceX = devianceX;
+ mLastDevianceY = devianceY;
+ mLastMaxXDeviance = maxXDeviance;
+ mLastMaxYDeviance = maxYDeviance;
+
logDebug("Straightness Deviance: (" + devianceX + "," + devianceY + ") vs "
+ "(" + maxXDeviance + "," + maxYDeviance + ")");
return devianceX > maxXDeviance || devianceY > maxYDeviance;
}
+ @Override
+ String getReason() {
+ return String.format(
+ (Locale) null,
+ "{devianceX=%f, maxDevianceX=%s, devianceY=%s, maxDevianceY=%s}",
+ mLastDevianceX, mLastMaxXDeviance, mLastDevianceY, mLastMaxYDeviance);
+ }
+
private float getAtan2LastPoint() {
MotionEvent firstEvent = getFirstMotionEvent();
MotionEvent lastEvent = getLastMotionEvent();
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
index c3672868ba32..4d3dc70f651c 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
@@ -33,7 +33,8 @@ import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.util.AsyncSensorManager;
+import com.android.systemui.util.sensors.AsyncSensorManager;
+import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.wakelock.DelayedWakeLock;
import com.android.systemui.util.wakelock.WakeLock;
@@ -45,7 +46,7 @@ public class DozeFactory {
/** Creates a DozeMachine with its parts for {@code dozeService}. */
public DozeMachine assembleMachine(DozeService dozeService, FalsingManager falsingManager) {
Context context = dozeService;
- SensorManager sensorManager = Dependency.get(AsyncSensorManager.class);
+ AsyncSensorManager sensorManager = Dependency.get(AsyncSensorManager.class);
AlarmManager alarmManager = context.getSystemService(AlarmManager.class);
DockManager dockManager = Dependency.get(DockManager.class);
WakefulnessLifecycle wakefulnessLifecycle = Dependency.get(WakefulnessLifecycle.class);
@@ -91,13 +92,14 @@ public class DozeFactory {
params.getPolicy());
}
- private DozeTriggers createDozeTriggers(Context context, SensorManager sensorManager,
+ private DozeTriggers createDozeTriggers(Context context, AsyncSensorManager sensorManager,
DozeHost host, AlarmManager alarmManager, AmbientDisplayConfiguration config,
DozeParameters params, Handler handler, WakeLock wakeLock, DozeMachine machine,
DockManager dockManager) {
boolean allowPulseTriggers = true;
return new DozeTriggers(context, machine, host, alarmManager, config, params,
- sensorManager, handler, wakeLock, allowPulseTriggers, dockManager);
+ sensorManager, handler, wakeLock, allowPulseTriggers, dockManager,
+ new ProximitySensor(context, sensorManager));
}
private DozeMachine.Part createDozeUi(Context context, DozeHost host, WakeLock wakeLock,
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 026a62528c8d..dfd83a552bf3 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -26,7 +26,6 @@ import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.hardware.Sensor;
-import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.TriggerEvent;
import android.hardware.TriggerEventListener;
@@ -43,11 +42,10 @@ import androidx.annotation.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
-import com.android.systemui.R;
import com.android.systemui.plugins.SensorManagerPlugin;
import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.util.AlarmTimeout;
-import com.android.systemui.util.AsyncSensorManager;
+import com.android.systemui.util.sensors.AsyncSensorManager;
+import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.wakelock.WakeLock;
import java.io.PrintWriter;
@@ -62,7 +60,7 @@ public class DozeSensors {
private final Context mContext;
private final AlarmManager mAlarmManager;
- private final SensorManager mSensorManager;
+ private final AsyncSensorManager mSensorManager;
private final ContentResolver mResolver;
private final TriggerSensor mPickupSensor;
private final DozeParameters mDozeParameters;
@@ -74,13 +72,13 @@ public class DozeSensors {
protected TriggerSensor[] mSensors;
private final Handler mHandler = new Handler();
- private final ProxSensor mProxSensor;
+ private final ProximitySensor mProximitySensor;
private long mDebounceFrom;
private boolean mSettingRegistered;
private boolean mListening;
private boolean mPaused;
- public DozeSensors(Context context, AlarmManager alarmManager, SensorManager sensorManager,
+ public DozeSensors(Context context, AlarmManager alarmManager, AsyncSensorManager sensorManager,
DozeParameters dozeParameters, AmbientDisplayConfiguration config, WakeLock wakeLock,
Callback callback, Consumer<Boolean> proxCallback, AlwaysOnDisplayPolicy policy) {
mContext = context;
@@ -91,6 +89,7 @@ public class DozeSensors {
mWakeLock = wakeLock;
mProxCallback = proxCallback;
mResolver = mContext.getContentResolver();
+ mCallback = callback;
boolean alwaysOn = mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT);
mSensors = new TriggerSensor[] {
@@ -146,8 +145,10 @@ public class DozeSensors {
false /* touchscreen */, mConfig.getWakeLockScreenDebounce()),
};
- mProxSensor = new ProxSensor(policy);
- mCallback = callback;
+ mProximitySensor = new ProximitySensor(context, sensorManager);
+
+ mProximitySensor.register(
+ proximityEvent -> mProxCallback.accept(!proximityEvent.getNear()));
}
/**
@@ -236,7 +237,15 @@ public class DozeSensors {
}
public void setProxListening(boolean listen) {
- mProxSensor.setRequested(listen);
+ if (mProximitySensor.isRegistered() && listen) {
+ mProximitySensor.alertListeners();
+ } else {
+ if (listen) {
+ mProximitySensor.resume();
+ } else {
+ mProximitySensor.pause();
+ }
+ }
}
private final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
@@ -267,115 +276,16 @@ public class DozeSensors {
/** Dump current state */
public void dump(PrintWriter pw) {
for (TriggerSensor s : mSensors) {
- pw.print(" Sensor: "); pw.println(s.toString());
+ pw.println(" Sensor: " + s.toString());
}
- pw.print(" ProxSensor: "); pw.println(mProxSensor.toString());
+ pw.println(" ProxSensor: " + mProximitySensor.toString());
}
/**
- * @return true if prox is currently far, false if near or null if unknown.
+ * @return true if prox is currently near, false if far or null if unknown.
*/
- public Boolean isProximityCurrentlyFar() {
- return mProxSensor.mCurrentlyFar;
- }
-
- private class ProxSensor implements SensorEventListener {
-
- boolean mRequested;
- boolean mRegistered;
- Boolean mCurrentlyFar;
- long mLastNear;
- final AlarmTimeout mCooldownTimer;
- final AlwaysOnDisplayPolicy mPolicy;
- final Sensor mSensor;
- final boolean mUsingBrightnessSensor;
-
- public ProxSensor(AlwaysOnDisplayPolicy policy) {
- mPolicy = policy;
- mCooldownTimer = new AlarmTimeout(mAlarmManager, this::updateRegistered,
- "prox_cooldown", mHandler);
-
- // The default prox sensor can be noisy, so let's use a prox gated brightness sensor
- // if available.
- 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);
- }
- mSensor = sensor;
- }
-
- void setRequested(boolean requested) {
- if (mRequested == requested) {
- // Send an update even if we don't re-register.
- mHandler.post(() -> {
- if (mCurrentlyFar != null) {
- mProxCallback.accept(mCurrentlyFar);
- }
- });
- return;
- }
- mRequested = requested;
- updateRegistered();
- }
-
- private void updateRegistered() {
- setRegistered(mRequested && !mCooldownTimer.isScheduled());
- }
-
- private void setRegistered(boolean register) {
- if (mRegistered == register) {
- return;
- }
- if (register) {
- mRegistered = mSensorManager.registerListener(this, mSensor,
- SensorManager.SENSOR_DELAY_NORMAL, mHandler);
- } else {
- mSensorManager.unregisterListener(this);
- mRegistered = false;
- mCurrentlyFar = null;
- }
- }
-
- @Override
- public void onSensorChanged(android.hardware.SensorEvent event) {
- if (DEBUG) Log.d(TAG, "onSensorChanged " + event);
-
- if (mUsingBrightnessSensor) {
- // The custom brightness sensor is gated by the proximity sensor and will return 0
- // whenever prox is covered.
- mCurrentlyFar = event.values[0] > 0;
- } else {
- mCurrentlyFar = event.values[0] >= event.sensor.getMaximumRange();
- }
- mProxCallback.accept(mCurrentlyFar);
-
- long now = SystemClock.elapsedRealtime();
- if (mCurrentlyFar == null) {
- // Sensor has been unregistered by the proxCallback. Do nothing.
- } else if (!mCurrentlyFar) {
- mLastNear = now;
- } else if (mCurrentlyFar && now - mLastNear < mPolicy.proxCooldownTriggerMs) {
- // If the last near was very recent, we might be using more power for prox
- // wakeups than we're saving from turning of the screen. Instead, turn it off
- // for a while.
- mCooldownTimer.schedule(mPolicy.proxCooldownPeriodMs,
- AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
- updateRegistered();
- }
- }
-
- @Override
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
- }
-
- @Override
- public String toString() {
- return String.format("{registered=%s, requested=%s, coolingDown=%s, currentlyFar=%s,"
- + " sensor=%s}", mRegistered, mRequested, mCooldownTimer.isScheduled(),
- mCurrentlyFar, mSensor);
- }
+ public Boolean isProximityCurrentlyNear() {
+ return mProximitySensor.isNear();
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index bab64db4519c..8eed71c21ce4 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -24,10 +24,6 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
-import android.hardware.SensorManager;
import android.hardware.display.AmbientDisplayConfiguration;
import android.metrics.LogMaker;
import android.os.Handler;
@@ -39,16 +35,16 @@ import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.util.Preconditions;
import com.android.systemui.Dependency;
-import com.android.systemui.R;
import com.android.systemui.dock.DockManager;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.Assert;
+import com.android.systemui.util.sensors.AsyncSensorManager;
+import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.wakelock.WakeLock;
import java.io.PrintWriter;
-import java.util.function.IntConsumer;
+import java.util.function.Consumer;
/**
* Handles triggers for ambient state changes.
@@ -67,20 +63,22 @@ public class DozeTriggers implements DozeMachine.Part {
*/
private static boolean sWakeDisplaySensorState = true;
+ private static final int PROXIMITY_TIMEOUT_DELAY_MS = 500;
+
private final Context mContext;
private final DozeMachine mMachine;
private final DozeSensors mDozeSensors;
private final DozeHost mDozeHost;
private final AmbientDisplayConfiguration mConfig;
private final DozeParameters mDozeParameters;
- private final SensorManager mSensorManager;
- private final Handler mHandler;
+ private final AsyncSensorManager mSensorManager;
private final WakeLock mWakeLock;
private final boolean mAllowPulseTriggers;
private final UiModeManager mUiModeManager;
private final TriggerReceiver mBroadcastReceiver = new TriggerReceiver();
private final DockEventListener mDockEventListener = new DockEventListener();
private final DockManager mDockManager;
+ private final ProximitySensor.ProximityCheck mProxCheck;
private long mNotificationPulseTime;
private boolean mPulsePending;
@@ -89,15 +87,15 @@ public class DozeTriggers implements DozeMachine.Part {
public DozeTriggers(Context context, DozeMachine machine, DozeHost dozeHost,
AlarmManager alarmManager, AmbientDisplayConfiguration config,
- DozeParameters dozeParameters, SensorManager sensorManager, Handler handler,
- WakeLock wakeLock, boolean allowPulseTriggers, DockManager dockManager) {
+ DozeParameters dozeParameters, AsyncSensorManager sensorManager, Handler handler,
+ WakeLock wakeLock, boolean allowPulseTriggers, DockManager dockManager,
+ ProximitySensor proximitySensor) {
mContext = context;
mMachine = machine;
mDozeHost = dozeHost;
mConfig = config;
mDozeParameters = dozeParameters;
mSensorManager = sensorManager;
- mHandler = handler;
mWakeLock = wakeLock;
mAllowPulseTriggers = allowPulseTriggers;
mDozeSensors = new DozeSensors(context, alarmManager, mSensorManager, dozeParameters,
@@ -105,6 +103,7 @@ public class DozeTriggers implements DozeMachine.Part {
dozeParameters.getPolicy());
mUiModeManager = mContext.getSystemService(UiModeManager.class);
mDockManager = dockManager;
+ mProxCheck = new ProximitySensor.ProximityCheck(proximitySensor, handler);
}
private void onNotification(Runnable onPulseSuppressedListener) {
@@ -134,25 +133,27 @@ public class DozeTriggers implements DozeMachine.Part {
}
}
- private void proximityCheckThenCall(IntConsumer callback,
+ private void proximityCheckThenCall(Consumer<Boolean> callback,
boolean alreadyPerformedProxCheck,
int reason) {
- Boolean cachedProxFar = mDozeSensors.isProximityCurrentlyFar();
+ Boolean cachedProxNear = mDozeSensors.isProximityCurrentlyNear();
if (alreadyPerformedProxCheck) {
- callback.accept(ProximityCheck.RESULT_NOT_CHECKED);
- } else if (cachedProxFar != null) {
- callback.accept(cachedProxFar ? ProximityCheck.RESULT_FAR : ProximityCheck.RESULT_NEAR);
+ callback.accept(null);
+ } else if (cachedProxNear != null) {
+ callback.accept(cachedProxNear);
} else {
final long start = SystemClock.uptimeMillis();
- new ProximityCheck() {
- @Override
- public void onProximityResult(int result) {
- final long end = SystemClock.uptimeMillis();
- DozeLog.traceProximityResult(mContext, result == RESULT_NEAR,
- end - start, reason);
- callback.accept(result);
- }
- }.check();
+ mProxCheck.check(PROXIMITY_TIMEOUT_DELAY_MS, near -> {
+ final long end = SystemClock.uptimeMillis();
+ DozeLog.traceProximityResult(
+ mContext,
+ near == null ? false : near,
+ end - start,
+ reason);
+ callback.accept(near);
+ mWakeLock.release(TAG);
+ });
+ mWakeLock.acquire(TAG);
}
}
@@ -178,7 +179,7 @@ public class DozeTriggers implements DozeMachine.Part {
}
} else {
proximityCheckThenCall((result) -> {
- if (result == ProximityCheck.RESULT_NEAR) {
+ if (result != null && result) {
// In pocket, drop event.
return;
}
@@ -267,7 +268,7 @@ public class DozeTriggers implements DozeMachine.Part {
if (wake) {
proximityCheckThenCall((result) -> {
- if (result == ProximityCheck.RESULT_NEAR) {
+ if (result != null && result) {
// In pocket, drop event.
return;
}
@@ -376,7 +377,7 @@ public class DozeTriggers implements DozeMachine.Part {
mPulsePending = true;
proximityCheckThenCall((result) -> {
- if (result == ProximityCheck.RESULT_NEAR) {
+ if (result != null && result) {
// in pocket, abort pulse
DozeLog.tracePulseDropped(mContext, "inPocket");
mPulsePending = false;
@@ -412,104 +413,11 @@ public class DozeTriggers implements DozeMachine.Part {
pw.print(" notificationPulseTime=");
pw.println(Formatter.formatShortElapsedTime(mContext, mNotificationPulseTime));
- pw.print(" pulsePending="); pw.println(mPulsePending);
+ pw.println(" pulsePending=" + mPulsePending);
pw.println("DozeSensors:");
mDozeSensors.dump(pw);
}
- /**
- * @see DozeSensors.ProxSensor
- */
- private abstract class ProximityCheck implements SensorEventListener, Runnable {
- private static final int TIMEOUT_DELAY_MS = 500;
-
- protected static final int RESULT_UNKNOWN = 0;
- protected static final int RESULT_NEAR = 1;
- protected static final int RESULT_FAR = 2;
- protected static final int RESULT_NOT_CHECKED = 3;
-
- private boolean mRegistered;
- private boolean mFinished;
- private float mMaxRange;
- private boolean mUsingBrightnessSensor;
-
- protected abstract void onProximityResult(int result);
-
- public void check() {
- 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);
- }
- if (sensor == null) {
- if (DozeMachine.DEBUG) Log.d(TAG, "ProxCheck: No sensor found");
- finishWithResult(RESULT_UNKNOWN);
- return;
- }
- mDozeSensors.setDisableSensorsInterferingWithProximity(true);
-
- mMaxRange = sensor.getMaximumRange();
- mSensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL, 0,
- mHandler);
- mHandler.postDelayed(this, TIMEOUT_DELAY_MS);
- mWakeLock.acquire(TAG);
- mRegistered = true;
- }
-
- /**
- * @see DozeSensors.ProxSensor#onSensorChanged(SensorEvent)
- */
- @Override
- public void onSensorChanged(SensorEvent event) {
- if (event.values.length == 0) {
- if (DozeMachine.DEBUG) Log.d(TAG, "ProxCheck: Event has no values!");
- finishWithResult(RESULT_UNKNOWN);
- } else {
- if (DozeMachine.DEBUG) {
- Log.d(TAG, "ProxCheck: Event: value=" + event.values[0] + " max=" + 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);
- }
- }
-
- @Override
- public void run() {
- if (DozeMachine.DEBUG) Log.d(TAG, "ProxCheck: No event received before timeout");
- finishWithResult(RESULT_UNKNOWN);
- }
-
- private void finishWithResult(int result) {
- if (mFinished) return;
- boolean wasRegistered = mRegistered;
- if (mRegistered) {
- mHandler.removeCallbacks(this);
- mSensorManager.unregisterListener(this);
- mDozeSensors.setDisableSensorsInterferingWithProximity(false);
- mRegistered = false;
- }
- onProximityResult(result);
- if (wasRegistered) {
- mWakeLock.release(TAG);
- }
- mFinished = true;
- }
-
- @Override
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
- // noop
- }
- }
-
private class TriggerReceiver extends BroadcastReceiver {
private boolean mRegistered;
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
index 30be7754cffc..6795bff6409a 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
@@ -18,6 +18,7 @@ package com.android.systemui.pip;
import static android.util.TypedValue.COMPLEX_UNIT_DIP;
+import android.content.ComponentName;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Point;
@@ -46,9 +47,6 @@ public class PipBoundsHandler {
private static final String TAG = PipBoundsHandler.class.getSimpleName();
private static final float INVALID_SNAP_FRACTION = -1f;
- // System.identityHashCode guarantees zero for null object.
- private static final int INVALID_SYSTEM_IDENTITY_TOKEN = 0;
-
private final Context mContext;
private final IWindowManager mWindowManager;
private final PipSnapAlgorithm mSnapAlgorithm;
@@ -58,7 +56,7 @@ public class PipBoundsHandler {
private final Point mTmpDisplaySize = new Point();
private IPinnedStackController mPinnedStackController;
- private int mLastPipToken;
+ private ComponentName mLastPipComponentName;
private float mReentrySnapFraction = INVALID_SNAP_FRACTION;
private float mDefaultAspectRatio;
@@ -80,8 +78,11 @@ public class PipBoundsHandler {
mContext = context;
mSnapAlgorithm = new PipSnapAlgorithm(context);
mWindowManager = WindowManagerGlobal.getWindowManagerService();
- mAspectRatio = mDefaultAspectRatio;
reloadResources();
+ // Initialize the aspect ratio to the default aspect ratio. Don't do this in reload
+ // resources as it would clobber mAspectRatio when entering PiP from fullscreen which
+ // triggers a configuration change and the resources to be reloaded.
+ mAspectRatio = mDefaultAspectRatio;
}
/**
@@ -161,27 +162,27 @@ public class PipBoundsHandler {
}
/**
- * Responds to IPinnedStackListener on saving reentry snap fraction for a given token.
- * Token should be generated via {@link System#identityHashCode(Object)}
+ * Responds to IPinnedStackListener on saving reentry snap fraction
+ * for a given {@link ComponentName}.
*/
- public void onSaveReentrySnapFraction(int token, Rect stackBounds) {
- mReentrySnapFraction = getSnapFraction(stackBounds);
- mLastPipToken = token;
+ public void onSaveReentrySnapFraction(ComponentName componentName, Rect bounds) {
+ mReentrySnapFraction = getSnapFraction(bounds);
+ mLastPipComponentName = componentName;
}
/**
- * Responds to IPinnedStackListener on resetting reentry snap fraction for a given token.
- * Token should be generated via {@link System#identityHashCode(Object)}
+ * Responds to IPinnedStackListener on resetting reentry snap fraction
+ * for a given {@link ComponentName}.
*/
- public void onResetReentrySnapFraction(int token) {
- if (mLastPipToken == token) {
+ public void onResetReentrySnapFraction(ComponentName componentName) {
+ if (componentName.equals(mLastPipComponentName)) {
onResetReentrySnapFractionUnchecked();
}
}
private void onResetReentrySnapFractionUnchecked() {
mReentrySnapFraction = INVALID_SNAP_FRACTION;
- mLastPipToken = INVALID_SYSTEM_IDENTITY_TOKEN;
+ mLastPipComponentName = null;
}
/**
@@ -212,24 +213,28 @@ public class PipBoundsHandler {
/**
* Responds to IPinnedStackListener on preparing the pinned stack animation.
*/
- public void onPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect stackBounds) {
- final Rect targetStackBounds;
- if (stackBounds == null) {
- targetStackBounds = getDefaultBounds(mReentrySnapFraction);
+ public void onPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect bounds) {
+ final Rect destinationBounds;
+ if (bounds == null) {
+ destinationBounds = getDefaultBounds(mReentrySnapFraction);
} else {
- targetStackBounds = new Rect();
- targetStackBounds.set(stackBounds);
+ destinationBounds = new Rect(bounds);
}
if (isValidPictureInPictureAspectRatio(aspectRatio)) {
- transformBoundsToAspectRatio(targetStackBounds, aspectRatio,
- true /* useCurrentMinEdgeSize */);
+ transformBoundsToAspectRatio(destinationBounds, aspectRatio,
+ false /* useCurrentMinEdgeSize */);
}
- if (targetStackBounds.equals(stackBounds)) {
+ if (destinationBounds.equals(bounds)) {
return;
}
mAspectRatio = aspectRatio;
onResetReentrySnapFractionUnchecked();
- // TODO: callback Window Manager on starting animation with calculated bounds
+ try {
+ mPinnedStackController.startAnimation(destinationBounds, sourceRectHint,
+ -1 /* animationDuration */);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to start PiP animation from SysUI", e);
+ }
}
/**
@@ -358,6 +363,7 @@ public class PipBoundsHandler {
public void dump(PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
pw.println(prefix + TAG);
+ pw.println(innerPrefix + "mLastPipComponentName=" + mLastPipComponentName);
pw.println(innerPrefix + "mReentrySnapFraction=" + mReentrySnapFraction);
pw.println(innerPrefix + "mDisplayInfo=" + mDisplayInfo);
pw.println(innerPrefix + "mDefaultAspectRatio=" + mDefaultAspectRatio);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index 3be3422a36ad..8dfae32a1939 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -32,14 +32,16 @@ import android.os.Handler;
import android.os.RemoteException;
import android.util.Log;
import android.util.Pair;
+import android.view.DisplayInfo;
import android.view.IPinnedStackController;
-import android.view.IPinnedStackListener;
import com.android.systemui.Dependency;
import com.android.systemui.UiOffloadThread;
import com.android.systemui.pip.BasePipManager;
+import com.android.systemui.pip.PipBoundsHandler;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.InputConsumerController;
+import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.WindowManagerWrapper;
@@ -58,8 +60,12 @@ public class PipManager implements BasePipManager {
private IActivityTaskManager mActivityTaskManager;
private Handler mHandler = new Handler();
- private final PinnedStackListener mPinnedStackListener = new PinnedStackListener();
+ private final PinnedStackListener mPinnedStackListener = new PipManagerPinnedStackListener();
+ private final DisplayInfo mTmpDisplayInfo = new DisplayInfo();
+ private final Rect mTmpInsetBounds = new Rect();
+ private final Rect mTmpNormalBounds = new Rect();
+ private PipBoundsHandler mPipBoundsHandler;
private InputConsumerController mInputConsumerController;
private PipMenuActivityController mMenuController;
private PipMediaController mMediaController;
@@ -119,11 +125,11 @@ public class PipManager implements BasePipManager {
/**
* Handler for messages from the PIP controller.
*/
- private class PinnedStackListener extends IPinnedStackListener.Stub {
-
+ private class PipManagerPinnedStackListener extends PinnedStackListener {
@Override
public void onListenerRegistered(IPinnedStackController controller) {
mHandler.post(() -> {
+ mPipBoundsHandler.setPinnedStackController(controller);
mTouchHandler.setPinnedStackController(controller);
});
}
@@ -131,6 +137,7 @@ public class PipManager implements BasePipManager {
@Override
public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
mHandler.post(() -> {
+ mPipBoundsHandler.onImeVisibilityChanged(imeVisible, imeHeight);
mTouchHandler.onImeVisibilityChanged(imeVisible, imeHeight);
});
}
@@ -138,31 +145,66 @@ public class PipManager implements BasePipManager {
@Override
public void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight) {
mHandler.post(() -> {
- mTouchHandler.onShelfVisibilityChanged(shelfVisible, shelfHeight);
+ mPipBoundsHandler.onShelfVisibilityChanged(shelfVisible, shelfHeight);
+ mTouchHandler.onShelfVisibilityChanged(shelfVisible, shelfHeight);
});
}
@Override
public void onMinimizedStateChanged(boolean isMinimized) {
mHandler.post(() -> {
+ mPipBoundsHandler.onMinimizedStateChanged(isMinimized);
mTouchHandler.setMinimizedState(isMinimized, true /* fromController */);
});
}
@Override
- public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds,
- Rect animatingBounds, boolean fromImeAdjustment, boolean fromShelfAdjustment,
- int displayRotation) {
+ public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment,
+ boolean fromShelfAdjustment) {
mHandler.post(() -> {
- mTouchHandler.onMovementBoundsChanged(insetBounds, normalBounds, animatingBounds,
- fromImeAdjustment, fromShelfAdjustment, displayRotation);
+ // Populate the inset / normal bounds and DisplayInfo from mPipBoundsHandler first.
+ mPipBoundsHandler.onMovementBoundsChanged(mTmpInsetBounds, mTmpNormalBounds,
+ animatingBounds, mTmpDisplayInfo);
+ mTouchHandler.onMovementBoundsChanged(mTmpInsetBounds, mTmpNormalBounds,
+ animatingBounds, fromImeAdjustment, fromShelfAdjustment,
+ mTmpDisplayInfo.rotation);
});
}
@Override
public void onActionsChanged(ParceledListSlice actions) {
+ mHandler.post(() -> mMenuController.setAppActions(actions));
+ }
+
+ @Override
+ public void onSaveReentrySnapFraction(ComponentName componentName, Rect bounds) {
+ mHandler.post(() -> mPipBoundsHandler.onSaveReentrySnapFraction(componentName, bounds));
+ }
+
+ @Override
+ public void onResetReentrySnapFraction(ComponentName componentName) {
+ mHandler.post(() -> mPipBoundsHandler.onResetReentrySnapFraction(componentName));
+ }
+
+ @Override
+ public void onDisplayInfoChanged(DisplayInfo displayInfo) {
+ mHandler.post(() -> mPipBoundsHandler.onDisplayInfoChanged(displayInfo));
+ }
+
+ @Override
+ public void onConfigurationChanged() {
+ mHandler.post(() -> mPipBoundsHandler.onConfigurationChanged());
+ }
+
+ @Override
+ public void onAspectRatioChanged(float aspectRatio) {
+ mHandler.post(() -> mPipBoundsHandler.onAspectRatioChanged(aspectRatio));
+ }
+
+ @Override
+ public void onPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect bounds) {
mHandler.post(() -> {
- mMenuController.setAppActions(actions);
+ mPipBoundsHandler.onPrepareAnimation(sourceRectHint, aspectRatio, bounds);
});
}
}
@@ -184,12 +226,13 @@ public class PipManager implements BasePipManager {
}
ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
+ mPipBoundsHandler = new PipBoundsHandler(context);
mInputConsumerController = InputConsumerController.getPipInputConsumer();
mMediaController = new PipMediaController(context, mActivityManager);
mMenuController = new PipMenuActivityController(context, mActivityManager, mMediaController,
mInputConsumerController);
mTouchHandler = new PipTouchHandler(context, mActivityManager, mActivityTaskManager,
- mMenuController, mInputConsumerController);
+ mMenuController, mInputConsumerController, mPipBoundsHandler);
mAppOpsListener = new PipAppOpsListener(context, mActivityManager,
mTouchHandler.getMotionHelper());
@@ -252,5 +295,6 @@ public class PipManager implements BasePipManager {
mInputConsumerController.dump(pw, innerPrefix);
mMenuController.dump(pw, innerPrefix);
mTouchHandler.dump(pw, innerPrefix);
+ mPipBoundsHandler.dump(pw, innerPrefix);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 30cf412671bc..1f36d97ce308 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -48,6 +48,7 @@ import android.view.accessibility.AccessibilityWindowInfo;
import com.android.internal.os.logging.MetricsLoggerWrapper;
import com.android.internal.policy.PipSnapAlgorithm;
import com.android.systemui.R;
+import com.android.systemui.pip.PipBoundsHandler;
import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.statusbar.FlingAnimationUtils;
@@ -75,6 +76,7 @@ public class PipTouchHandler {
private final IActivityTaskManager mActivityTaskManager;
private final ViewConfiguration mViewConfig;
private final PipMenuListener mMenuListener = new PipMenuListener();
+ private final PipBoundsHandler mPipBoundsHandler;
private IPinnedStackController mPinnedStackController;
private final PipMenuActivityController mMenuController;
@@ -178,7 +180,8 @@ public class PipTouchHandler {
public PipTouchHandler(Context context, IActivityManager activityManager,
IActivityTaskManager activityTaskManager, PipMenuActivityController menuController,
- InputConsumerController inputConsumerController) {
+ InputConsumerController inputConsumerController,
+ PipBoundsHandler pipBoundsHandler) {
// Initialize the Pip input consumer
mContext = context;
@@ -211,6 +214,8 @@ public class PipTouchHandler {
inputConsumerController.setInputListener(this::handleTouchEvent);
inputConsumerController.setRegistrationListener(this::onRegistrationChanged);
onRegistrationChanged(inputConsumerController.isRegistered());
+
+ mPipBoundsHandler = pipBoundsHandler;
}
public void setTouchEnabled(boolean enabled) {
@@ -787,14 +792,8 @@ public class PipTouchHandler {
mMovementBounds = isMenuExpanded
? mExpandedMovementBounds
: mNormalMovementBounds;
- try {
- if (mPinnedStackController != null) {
- mPinnedStackController.setMinEdgeSize(
- isMenuExpanded ? mExpandedShortestEdgeSize : 0);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Could not set minimized state", e);
- }
+ mPipBoundsHandler.setMinEdgeSize(
+ isMenuExpanded ? mExpandedShortestEdgeSize : 0);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index 918af4f0cd4c..81d6973efb2a 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -19,13 +19,10 @@ package com.android.systemui.pip.tv;
import static android.app.ActivityTaskManager.INVALID_STACK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.view.Display.DEFAULT_DISPLAY;
-import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityManager.StackInfo;
import android.app.ActivityTaskManager;
-import android.app.IActivityManager;
import android.app.IActivityTaskManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -46,16 +43,15 @@ import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
-import android.view.IPinnedStackController;
-import android.view.IPinnedStackListener;
-import android.view.IWindowManager;
-import android.view.WindowManagerGlobal;
+import android.view.DisplayInfo;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.UiOffloadThread;
import com.android.systemui.pip.BasePipManager;
+import com.android.systemui.pip.PipBoundsHandler;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.WindowManagerWrapper;
@@ -111,9 +107,8 @@ public class PipManager implements BasePipManager {
private int mSuspendPipResizingReason;
private Context mContext;
- private IActivityManager mActivityManager;
+ private PipBoundsHandler mPipBoundsHandler;
private IActivityTaskManager mActivityTaskManager;
- private IWindowManager mWindowManager;
private MediaSessionManager mMediaSessionManager;
private int mState = STATE_NO_PIP;
private int mResumeResizePinnedStackRunnableState = STATE_NO_PIP;
@@ -135,11 +130,16 @@ public class PipManager implements BasePipManager {
private PipNotification mPipNotification;
private ParceledListSlice mCustomActions;
+ // Used to calculate the movement bounds
+ private final DisplayInfo mTmpDisplayInfo = new DisplayInfo();
+ private final Rect mTmpInsetBounds = new Rect();
+ private final Rect mTmpNormalBounds = new Rect();
+
// Keeps track of the IME visibility to adjust the PiP when the IME is visible
private boolean mImeVisible;
private int mImeHeightAdjustment;
- private final PinnedStackListener mPinnedStackListener = new PinnedStackListener();
+ private final PinnedStackListener mPinnedStackListener = new PipManagerPinnedStackListener();
private final Runnable mResizePinnedStackRunnable = new Runnable() {
@Override
@@ -181,11 +181,7 @@ public class PipManager implements BasePipManager {
/**
* Handler for messages from the PIP controller.
*/
- private class PinnedStackListener extends IPinnedStackListener.Stub {
-
- @Override
- public void onListenerRegistered(IPinnedStackController controller) {}
-
+ private class PipManagerPinnedStackListener extends PinnedStackListener {
@Override
public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
if (mState == STATE_PIP) {
@@ -205,17 +201,13 @@ public class PipManager implements BasePipManager {
}
@Override
- public void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight) {}
-
- @Override
- public void onMinimizedStateChanged(boolean isMinimized) {}
-
- @Override
- public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds,
- Rect animatingBounds, boolean fromImeAdjustment, boolean fromShelfAdjustment,
- int displayRotation) {
+ public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment,
+ boolean fromShelfAdjustment) {
mHandler.post(() -> {
- mDefaultPipBounds.set(normalBounds);
+ // Populate the inset / normal bounds and DisplayInfo from mPipBoundsHandler first.
+ mPipBoundsHandler.onMovementBoundsChanged(mTmpInsetBounds, mTmpNormalBounds,
+ animatingBounds, mTmpDisplayInfo);
+ mDefaultPipBounds.set(animatingBounds);
});
}
@@ -241,10 +233,8 @@ public class PipManager implements BasePipManager {
}
mInitialized = true;
mContext = context;
-
- mActivityManager = ActivityManager.getService();
+ mPipBoundsHandler = new PipBoundsHandler(context);
mActivityTaskManager = ActivityTaskManager.getService();
- mWindowManager = WindowManagerGlobal.getWindowManagerService();
ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_MEDIA_RESOURCE_GRANTED);
@@ -291,7 +281,7 @@ public class PipManager implements BasePipManager {
(MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
try {
- mWindowManager.registerPinnedStackListener(DEFAULT_DISPLAY, mPinnedStackListener);
+ WindowManagerWrapper.getInstance().addPinnedStackListener(mPinnedStackListener);
} catch (RemoteException e) {
Log.e(TAG, "Failed to register pinned stack listener", e);
}
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 147633b0ea25..55ae61de5bc6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java
@@ -16,11 +16,13 @@
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;
@@ -53,6 +55,7 @@ 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];
@@ -65,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
@@ -115,8 +121,7 @@ public class QSCarrierGroup extends LinearLayout implements
return;
}
mListening = listening;
- // TODO(b/140053526)
- whitelistIpcs(this::updateListeners);
+ mBgHandler.post(this::updateListeners);
}
@Override
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 61d7498ced94..1e763cf79240 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -222,7 +222,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
if (!TILES_SETTING.equals(key)) {
return;
}
- if (DEBUG) Log.d(TAG, "Recreating tiles");
+ Log.d(TAG, "Recreating tiles");
if (newValue == null && UserManager.isDeviceInDemoMode(mContext)) {
newValue = mContext.getResources().getString(R.string.quick_settings_tiles_retail_mode);
}
@@ -231,7 +231,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
if (tileSpecs.equals(mTileSpecs) && currentUser == mCurrentUser) return;
mTiles.entrySet().stream().filter(tile -> !tileSpecs.contains(tile.getKey())).forEach(
tile -> {
- if (DEBUG) Log.d(TAG, "Destroying tile: " + tile.getKey());
+ Log.d(TAG, "Destroying tile: " + tile.getKey());
tile.getValue().destroy();
});
final LinkedHashMap<String, QSTile> newTiles = new LinkedHashMap<>();
@@ -248,9 +248,10 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
newTiles.put(tileSpec, tile);
} else {
tile.destroy();
+ Log.d(TAG, "Destroying not available tile: " + tileSpec);
}
} else {
- if (DEBUG) Log.d(TAG, "Creating tile: " + tileSpec);
+ Log.d(TAG, "Creating tile: " + tileSpec);
try {
tile = createTile(tileSpec);
if (tile != null) {
@@ -259,6 +260,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
newTiles.put(tileSpec, tile);
} else {
tile.destroy();
+ Log.d(TAG, "Destroying not available tile: " + tileSpec);
}
}
} catch (Throwable t) {
@@ -274,7 +276,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
mTiles.putAll(newTiles);
if (newTiles.isEmpty() && !tileSpecs.isEmpty()) {
// If we didn't manage to create any tiles, set it to empty (default)
- if (DEBUG) Log.d(TAG, "No valid tiles on tuning changed. Setting to default.");
+ Log.d(TAG, "No valid tiles on tuning changed. Setting to default.");
changeTiles(currentSpecs, loadTileSpecs(mContext, ""));
} else {
for (int i = 0; i < mCallbacks.size(); i++) {
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/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
index f4a516261ade..8b7f280608a5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -36,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;
@@ -43,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;
@@ -67,6 +69,7 @@ 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;
@@ -106,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);
@@ -161,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,
@@ -214,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);
@@ -352,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/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/MediaTransferManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/MediaTransferManager.java
index 752b6a80f93d..926ae71194d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/MediaTransferManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/MediaTransferManager.java
@@ -18,12 +18,19 @@ package com.android.systemui.statusbar;
import android.content.Context;
import android.content.Intent;
+import android.content.res.ColorStateList;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.RippleDrawable;
import android.service.notification.StatusBarNotification;
import android.util.FeatureFlagUtils;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import com.android.internal.R;
import com.android.settingslib.media.MediaOutputSliceConstants;
import com.android.systemui.Dependency;
import com.android.systemui.plugins.ActivityStarter;
@@ -51,7 +58,8 @@ public class MediaTransferManager {
}
ViewParent parent = view.getParent();
- StatusBarNotification statusBarNotification = getNotificationForParent(parent);
+ StatusBarNotification statusBarNotification =
+ getRowForParent(parent).getStatusBarNotification();
final Intent intent = new Intent()
.setAction(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT)
.putExtra(MediaOutputSliceConstants.EXTRA_PACKAGE_NAME,
@@ -60,16 +68,6 @@ public class MediaTransferManager {
Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
return true;
}
-
- private StatusBarNotification getNotificationForParent(ViewParent parent) {
- while (parent != null) {
- if (parent instanceof ExpandableNotificationRow) {
- return ((ExpandableNotificationRow) parent).getStatusBarNotification();
- }
- parent = parent.getParent();
- }
- return null;
- }
};
public MediaTransferManager(Context context) {
@@ -77,6 +75,16 @@ public class MediaTransferManager {
mActivityStarter = Dependency.get(ActivityStarter.class);
}
+ private ExpandableNotificationRow getRowForParent(ViewParent parent) {
+ while (parent != null) {
+ if (parent instanceof ExpandableNotificationRow) {
+ return ((ExpandableNotificationRow) parent);
+ }
+ parent = parent.getParent();
+ }
+ return null;
+ }
+
/**
* apply the action button for MediaTransfer
*
@@ -95,5 +103,23 @@ public class MediaTransferManager {
view.setVisibility(View.VISIBLE);
view.setOnClickListener(mOnClickHandler);
+
+ ExpandableNotificationRow enr = getRowForParent(view.getParent());
+ int color = enr.getNotificationHeader().getOriginalIconColor();
+ ColorStateList tintList = ColorStateList.valueOf(color);
+
+ // Update the outline color
+ LinearLayout viewLayout = (LinearLayout) view;
+ RippleDrawable bkgDrawable = (RippleDrawable) viewLayout.getBackground();
+ GradientDrawable rect = (GradientDrawable) bkgDrawable.getDrawable(0);
+ rect.setStroke(2, color);
+
+ // Update the image color
+ ImageView image = view.findViewById(R.id.media_seamless_image);
+ image.setImageTintList(tintList);
+
+ // Update the text color
+ TextView text = view.findViewById(R.id.media_seamless_text);
+ text.setTextColor(tintList);
}
}
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/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
index 276afa7a3a94..a70dc7c0ec5d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
@@ -33,6 +33,7 @@ import com.android.systemui.Gefingerpoken
import com.android.systemui.Interpolators
import com.android.systemui.R
import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
@@ -56,7 +57,8 @@ constructor(
private val wakeUpCoordinator: NotificationWakeUpCoordinator,
private val bypassController: KeyguardBypassController,
private val headsUpManager: HeadsUpManagerPhone,
- private val roundnessManager: NotificationRoundnessManager
+ private val roundnessManager: NotificationRoundnessManager,
+ private val statusBarStateController: StatusBarStateController
) : Gefingerpoken {
companion object {
private val RUBBERBAND_FACTOR_STATIC = 0.25f
@@ -188,7 +190,8 @@ constructor(
MotionEvent.ACTION_MOVE -> updateExpansionHeight(moveDistance)
MotionEvent.ACTION_UP -> {
velocityTracker!!.computeCurrentVelocity(1000 /* units */)
- val canExpand = moveDistance > 0 && velocityTracker!!.getYVelocity() > -1000
+ val canExpand = moveDistance > 0 && velocityTracker!!.getYVelocity() > -1000 &&
+ statusBarStateController.state != StatusBarState.SHADE
if (!mFalsingManager.isUnlockingDisabled && !isFalseTouch && canExpand) {
finishExpansion()
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index 4422a81874c6..8b9268e1888a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -20,6 +20,7 @@ import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.text.format.DateFormat;
import android.util.FloatProperty;
+import android.util.Log;
import android.view.View;
import android.view.animation.Interpolator;
@@ -137,6 +138,11 @@ public class StatusBarStateControllerImpl implements SysuiStatusBarStateControll
// Record the to-be mState and mLastState
recordHistoricalState(state, mState);
+ // b/139259891
+ if (mState == StatusBarState.SHADE && state == StatusBarState.SHADE_LOCKED) {
+ Log.e(TAG, "Invalid state transition: SHADE -> SHADE_LOCKED", new Throwable());
+ }
+
synchronized (mListeners) {
String tag = getClass().getSimpleName() + "#setState(" + state + ")";
DejankUtils.startDetectingBlockingIpcs(tag);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
index 91d47077fc31..13c6f2730d08 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
@@ -34,7 +34,6 @@ import android.view.View;
import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.systemui.Interpolators;
-import com.android.systemui.shared.system.SurfaceControlCompat;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
@@ -145,6 +144,7 @@ public class ActivityLaunchAnimator {
@Override
public void onAnimationStart(RemoteAnimationTarget[] remoteAnimationTargets,
+ RemoteAnimationTarget[] remoteAnimationWallpaperTargets,
IRemoteAnimationFinishedCallback iRemoteAnimationFinishedCallback)
throws RemoteException {
mSourceNotification.post(() -> {
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 4a27a4ed31a1..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}. */
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/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 d1b05603223d..c3211e307845 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;
@@ -38,6 +41,7 @@ import android.app.Person;
import android.content.Context;
import android.graphics.drawable.Icon;
import android.os.Bundle;
+import android.os.Parcelable;
import android.os.SystemClock;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.SnoozeCriterion;
@@ -58,6 +62,7 @@ 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.List;
@@ -99,6 +104,7 @@ public final class NotificationEntry {
public int targetSdk;
private long lastFullScreenIntentLaunchTime = NOT_LAUNCHED_YET;
public CharSequence remoteInputText;
+ private final List<Person> mAssociatedPeople = new ArrayList<>();
/**
* If {@link android.app.RemoteInput#getEditChoicesBeforeSending} is enabled, and the user is
@@ -135,48 +141,29 @@ public final class NotificationEntry {
private boolean hasSentReply;
/**
+ * Whether this notification has changed in visual appearance since the previous post.
+ * New notifications are interruptive by default.
+ */
+ public boolean isVisuallyInterruptive;
+
+ /**
* 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 without a ranking.
- *
- * @deprecated Use NotificationEntryBuilder instead.
- */
- @VisibleForTesting
- @Deprecated
- public static NotificationEntry buildForTest(StatusBarNotification sbn) {
- return new NotificationEntry(sbn, null, true);
+ setNotification(sbn);
+ setRanking(ranking);
}
/** The key for this notification. Guaranteed to be immutable and unique */
@@ -197,11 +184,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();
}
/**
@@ -218,7 +206,12 @@ 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;
+ isVisuallyInterruptive = ranking.visuallyInterruptive();
}
public NotificationChannel getChannel() {
@@ -258,6 +251,7 @@ public final class NotificationEntry {
return mRanking.canBubble();
}
+
public @NonNull List<Notification.Action> getSmartActions() {
return mRanking.getSmartActions();
}
@@ -283,20 +277,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;
}
/**
@@ -315,6 +328,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;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index 48a82957bf1e..a612a1721c41 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -264,7 +264,7 @@ public class NotificationContentInflater {
mIsChildInGroup, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight,
packageContext);
result = inflateSmartReplyViews(result, reInflateFlags, mRow.getEntry(),
- mRow.getContext(), mRow.getHeadsUpManager(),
+ mRow.getContext(), packageContext, mRow.getHeadsUpManager(),
mRow.getExistingSmartRepliesAndActions());
apply(
inflateSynchronously,
@@ -311,20 +311,21 @@ public class NotificationContentInflater {
private static InflationProgress inflateSmartReplyViews(InflationProgress result,
@InflationFlag int reInflateFlags, NotificationEntry entry, Context context,
- HeadsUpManager headsUpManager, SmartRepliesAndActions previousSmartRepliesAndActions) {
+ Context packageContext, HeadsUpManager headsUpManager,
+ SmartRepliesAndActions previousSmartRepliesAndActions) {
SmartReplyConstants smartReplyConstants = Dependency.get(SmartReplyConstants.class);
SmartReplyController smartReplyController = Dependency.get(SmartReplyController.class);
if ((reInflateFlags & FLAG_CONTENT_VIEW_EXPANDED) != 0 && result.newExpandedView != null) {
result.expandedInflatedSmartReplies =
InflatedSmartReplies.inflate(
- context, entry, smartReplyConstants, smartReplyController,
- headsUpManager, previousSmartRepliesAndActions);
+ context, packageContext, entry, smartReplyConstants,
+ smartReplyController, headsUpManager, previousSmartRepliesAndActions);
}
if ((reInflateFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0 && result.newHeadsUpView != null) {
result.headsUpInflatedSmartReplies =
InflatedSmartReplies.inflate(
- context, entry, smartReplyConstants, smartReplyController,
- headsUpManager, previousSmartRepliesAndActions);
+ context, packageContext, entry, smartReplyConstants,
+ smartReplyController, headsUpManager, previousSmartRepliesAndActions);
}
return result;
}
@@ -817,7 +818,7 @@ public class NotificationContentInflater {
recoveredBuilder, mIsLowPriority, mIsChildInGroup, mUsesIncreasedHeight,
mUsesIncreasedHeadsUpHeight, packageContext);
return inflateSmartReplyViews(inflationProgress, mReInflateFlags, mRow.getEntry(),
- mRow.getContext(), mRow.getHeadsUpManager(),
+ mRow.getContext(), packageContext, mRow.getHeadsUpManager(),
mRow.getExistingSmartRepliesAndActions());
} catch (Exception e) {
mError = e;
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 cc2078bd158b..f30a8b12ab39 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
@@ -1139,19 +1139,21 @@ public class NotificationContentView extends FrameLayout {
}
private void applyMediaTransfer(final NotificationEntry entry) {
- View bigContentView = mExpandedChild;
- if (bigContentView == null || !entry.isMediaNotification()) {
+ if (!entry.isMediaNotification()) {
return;
}
- View mediaActionContainer = bigContentView.findViewById(
- com.android.internal.R.id.media_actions);
- if (!(mediaActionContainer instanceof LinearLayout)) {
- return;
+ View bigContentView = mExpandedChild;
+ if (bigContentView != null && (bigContentView instanceof ViewGroup)) {
+ mMediaTransferManager.applyMediaTransferView((ViewGroup) bigContentView,
+ entry);
}
- mMediaTransferManager.applyMediaTransferView((ViewGroup) mediaActionContainer,
- entry);
+ View smallContentView = mContractedChild;
+ if (smallContentView != null && (smallContentView instanceof ViewGroup)) {
+ mMediaTransferManager.applyMediaTransferView((ViewGroup) smallContentView,
+ entry);
+ }
}
private void applyRemoteInputAndSmartReply(final NotificationEntry entry) {
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 1c9b2256af9b..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,7 +5795,7 @@ 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().isAmbient();
}
@@ -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/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 41c6a7ba7848..2d012c93f42b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -448,9 +448,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback {
} else if (!unlockingAllowed) {
return bypass ? MODE_SHOW_BOUNCER : MODE_NONE;
} else if (mDozeScrimController.isPulsing()) {
- // Let's not wake-up to lock screen when not bypassing, otherwise the notification
- // would move as the user tried to tap it.
- return bypass ? MODE_WAKE_AND_UNLOCK_PULSING : MODE_NONE;
+ return bypass ? MODE_WAKE_AND_UNLOCK_PULSING : MODE_ONLY_WAKE;
} else {
if (bypass) {
// Wake-up fading out nicely
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
index 575b5597b657..4cd3ad27ab34 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
@@ -18,7 +18,6 @@ package com.android.systemui.statusbar.phone;
import static android.view.Display.INVALID_DISPLAY;
import android.content.Context;
-import android.content.pm.ParceledListSlice;
import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.graphics.Point;
@@ -36,8 +35,6 @@ import android.util.Log;
import android.util.MathUtils;
import android.util.StatsLog;
import android.view.Gravity;
-import android.view.IPinnedStackController;
-import android.view.IPinnedStackListener;
import android.view.ISystemGestureExclusionListener;
import android.view.InputChannel;
import android.view.InputDevice;
@@ -57,6 +54,7 @@ import com.android.systemui.R;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.model.SysUiState;
import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.WindowManagerWrapper;
@@ -72,35 +70,13 @@ public class EdgeBackGestureHandler implements DisplayListener {
private static final int MAX_LONG_PRESS_TIMEOUT = SystemProperties.getInt(
"gestures.back_timeout", 250);
- private final IPinnedStackListener.Stub mImeChangedListener = new IPinnedStackListener.Stub() {
- @Override
- public void onListenerRegistered(IPinnedStackController controller) {
- }
-
+ private final PinnedStackListener mImeChangedListener = new PinnedStackListener() {
@Override
public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
// No need to thread jump, assignments are atomic
mImeHeight = imeVisible ? imeHeight : 0;
// TODO: Probably cancel any existing gesture
}
-
- @Override
- public void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight) {
- }
-
- @Override
- public void onMinimizedStateChanged(boolean isMinimized) {
- }
-
- @Override
- public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds,
- Rect animatingBounds, boolean fromImeAdjustment, boolean fromShelfAdjustment,
- int displayRotation) {
- }
-
- @Override
- public void onActionsChanged(ParceledListSlice actions) {
- }
};
private ISystemGestureExclusionListener mGestureExclusionListener =
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/KeyguardLiftController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
index f7bb97b38dd5..00b764bfbe9b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
@@ -24,7 +24,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.Dependency
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.util.Assert
-import com.android.systemui.util.AsyncSensorManager
+import com.android.systemui.util.sensors.AsyncSensorManager
class KeyguardLiftController constructor(
private val statusBarStateController: StatusBarStateController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index ba3406999388..1a3560ece1d7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -537,11 +537,7 @@ public class NotificationIconAreaController implements DarkReceiver,
if (dozeParameters.shouldControlScreenOff()) {
mAodIcons.setTranslationY(-mAodIconAppearTranslation);
mAodIcons.setAlpha(0);
- mAodIcons.animate()
- .setInterpolator(Interpolators.DECELERATE_QUINT)
- .translationY(0)
- .setDuration(AOD_ICONS_APPEAR_DURATION)
- .start();
+ animateInAodIconTranslation();
mAodIcons.animate()
.alpha(1)
.setInterpolator(Interpolators.LINEAR)
@@ -550,6 +546,14 @@ public class NotificationIconAreaController implements DarkReceiver,
}
}
+ private void animateInAodIconTranslation() {
+ mAodIcons.animate()
+ .setInterpolator(Interpolators.DECELERATE_QUINT)
+ .translationY(0)
+ .setDuration(AOD_ICONS_APPEAR_DURATION)
+ .start();
+ }
+
private void reloadAodColor() {
mAodIconTint = Utils.getColorAttrDefaultColor(mContext,
R.attr.wallpaperTextColor);
@@ -606,14 +610,19 @@ public class NotificationIconAreaController implements DarkReceiver,
mAodIcons.setAlpha(1.0f);
appearAodIcons();
} else {
+ // Let's make sure the icon are translated to 0, since we cancelled it above
+ animateInAodIconTranslation();
// We were fading out, let's fade in instead
CrossFadeHelper.fadeIn(mAodIcons);
}
} else {
+ // Let's make sure the icon are translated to 0, since we cancelled it above
+ animateInAodIconTranslation();
CrossFadeHelper.fadeOut(mAodIcons);
}
} else {
mAodIcons.setAlpha(1.0f);
+ mAodIcons.setTranslationY(0);
mAodIcons.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
}
}
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..8c927799c31c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -473,11 +473,11 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
if (mDarkenWhileDragging) {
mBehindAlpha = MathUtils.lerp(GRADIENT_SCRIM_ALPHA_BUSY, alphaBehind,
interpolatedFract);
- mInFrontAlpha = 0;
+ mInFrontAlpha = mState.getFrontAlpha();
} else {
mBehindAlpha = MathUtils.lerp(0 /* start */, alphaBehind,
interpolatedFract);
- mInFrontAlpha = 0;
+ mInFrontAlpha = mState.getFrontAlpha();
}
mBehindTint = ColorUtils.blendARGB(ScrimState.BOUNCER.getBehindTint(),
mState.getBehindTint(), interpolatedFract);
@@ -503,13 +503,14 @@ 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();
}
mState.AOD.setAodFrontScrimAlpha(alpha);
+ mState.PULSING.setAodFrontScrimAlpha(alpha);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index c9acbad1e8cf..7463c7c232bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -139,7 +139,7 @@ public enum ScrimState {
PULSING(5) {
@Override
public void prepare(ScrimState previousState) {
- mFrontAlpha = 0f;
+ mFrontAlpha = mAodFrontScrimAlpha;
mBubbleAlpha = 0f;
mBehindTint = Color.BLACK;
mFrontTint = Color.BLACK;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
index 40c3d9d19c16..0a2fb2e783a9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.policy;
import static com.android.systemui.Dependency.BG_LOOPER_NAME;
+import static com.android.systemui.Dependency.MAIN_LOOPER_NAME;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -67,16 +68,18 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa
private boolean mEnabled;
private int mConnectionState = BluetoothAdapter.STATE_DISCONNECTED;
- private final H mHandler = new H(Looper.getMainLooper());
+ private final H mHandler;
private int mState;
/**
*/
@Inject
public BluetoothControllerImpl(Context context, @Named(BG_LOOPER_NAME) Looper bgLooper,
+ @Named(MAIN_LOOPER_NAME) Looper mainLooper,
@Nullable LocalBluetoothManager localBluetoothManager) {
mLocalBluetoothManager = localBluetoothManager;
mBgHandler = new Handler(bgLooper);
+ mHandler = new H(mainLooper);
if (mLocalBluetoothManager != null) {
mLocalBluetoothManager.getEventManager().registerCallback(this);
mLocalBluetoothManager.getProfileManager().addServiceListener(this);
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 25a32b330f7d..be27741b4dc8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplies.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplies.java
@@ -80,6 +80,7 @@ public class InflatedSmartReplies {
*/
public static InflatedSmartReplies inflate(
Context context,
+ Context packageContext,
NotificationEntry entry,
SmartReplyConstants smartReplyConstants,
SmartReplyController smartReplyController,
@@ -108,9 +109,9 @@ public class InflatedSmartReplies {
}
if (newSmartRepliesAndActions.smartActions != null) {
suggestionButtons.addAll(
- smartReplyView.inflateSmartActions(newSmartRepliesAndActions.smartActions,
- smartReplyController, entry, headsUpManager,
- delayOnClickListener));
+ smartReplyView.inflateSmartActions(packageContext,
+ newSmartRepliesAndActions.smartActions, smartReplyController, entry,
+ headsUpManager, delayOnClickListener));
}
return new InflatedSmartReplies(smartReplyView, suggestionButtons,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index dbfb09f7fc41..5da726749580 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -294,7 +294,8 @@ public class MobileSignalController extends SignalController<
}
boolean dataDisabled = mCurrentState.userSetup
&& (mCurrentState.iconGroup == TelephonyIcons.DATA_DISABLED
- || mCurrentState.iconGroup == TelephonyIcons.NOT_DEFAULT_DATA);
+ || (mCurrentState.iconGroup == TelephonyIcons.NOT_DEFAULT_DATA
+ && mCurrentState.defaultDataOff));
boolean noInternet = mCurrentState.inetCondition == 0;
boolean cutOut = dataDisabled || noInternet;
return SignalDrawable.getState(level, getNumLevels(), cutOut);
@@ -320,7 +321,7 @@ public class MobileSignalController extends SignalController<
dataContentDescription = mContext.getString(R.string.data_connection_no_internet);
}
final boolean dataDisabled = (mCurrentState.iconGroup == TelephonyIcons.DATA_DISABLED
- || mCurrentState.iconGroup == TelephonyIcons.NOT_DEFAULT_DATA)
+ || (mCurrentState.iconGroup == TelephonyIcons.NOT_DEFAULT_DATA))
&& mCurrentState.userSetup;
// Show icon in QS when we are connected or data is disabled.
@@ -484,6 +485,7 @@ public class MobileSignalController extends SignalController<
Log.d(mTag, "updateTelephonySignalStrength: hasService=" +
Utils.isInService(mServiceState) + " ss=" + mSignalStrength);
}
+ checkDefaultData();
mCurrentState.connected = Utils.isInService(mServiceState)
&& mSignalStrength != null;
if (mCurrentState.connected) {
@@ -541,6 +543,23 @@ public class MobileSignalController extends SignalController<
notifyListenersIfNecessary();
}
+ /**
+ * If we are controlling the NOT_DEFAULT_DATA icon, check the status of the other one
+ */
+ private void checkDefaultData() {
+ if (mCurrentState.iconGroup != TelephonyIcons.NOT_DEFAULT_DATA) {
+ mCurrentState.defaultDataOff = false;
+ return;
+ }
+
+ mCurrentState.defaultDataOff = mNetworkController.isDataControllerDisabled();
+ }
+
+ void onMobileDataChanged() {
+ checkDefaultData();
+ notifyListenersIfNecessary();
+ }
+
private MobileIconGroup getNr5GIconGroup() {
if (mServiceState == null) return null;
@@ -617,7 +636,7 @@ public class MobileSignalController extends SignalController<
return candidateIconGroup;
}
- private boolean isDataDisabled() {
+ boolean isDataDisabled() {
return !mPhone.isDataCapable();
}
@@ -750,6 +769,7 @@ public class MobileSignalController extends SignalController<
boolean isDefault;
boolean userSetup;
boolean roaming;
+ boolean defaultDataOff; // Tracks the on/off state of the defaultDataSubscription
@Override
public void copyFrom(State s) {
@@ -765,6 +785,7 @@ public class MobileSignalController extends SignalController<
carrierNetworkChangeMode = state.carrierNetworkChangeMode;
userSetup = state.userSetup;
roaming = state.roaming;
+ defaultDataOff = state.defaultDataOff;
}
@Override
@@ -781,7 +802,8 @@ public class MobileSignalController extends SignalController<
builder.append("airplaneMode=").append(airplaneMode).append(',');
builder.append("carrierNetworkChangeMode=").append(carrierNetworkChangeMode)
.append(',');
- builder.append("userSetup=").append(userSetup);
+ builder.append("userSetup=").append(userSetup).append(',');
+ builder.append("defaultDataOff=").append(defaultDataOff);
}
@Override
@@ -796,7 +818,8 @@ public class MobileSignalController extends SignalController<
&& ((MobileState) o).carrierNetworkChangeMode == carrierNetworkChangeMode
&& ((MobileState) o).userSetup == userSetup
&& ((MobileState) o).isDefault == isDefault
- && ((MobileState) o).roaming == roaming;
+ && ((MobileState) o).roaming == roaming
+ && ((MobileState) o).defaultDataOff == defaultDataOff;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index e1b3816cf759..7b5d48947498 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -220,6 +220,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
@Override
public void onMobileDataEnabled(boolean enabled) {
mCallbackHandler.setMobileDataEnabled(enabled);
+ notifyControllersMobileDataChanged();
}
});
mWifiSignalController = new WifiSignalController(mContext, mHasMobileDataFeature,
@@ -386,6 +387,22 @@ public class NetworkControllerImpl extends BroadcastReceiver
return mMobileSignalControllers.size();
}
+ boolean isDataControllerDisabled() {
+ MobileSignalController dataController = getDataController();
+ if (dataController == null) {
+ return false;
+ }
+
+ return dataController.isDataDisabled();
+ }
+
+ private void notifyControllersMobileDataChanged() {
+ for (int i = 0; i < mMobileSignalControllers.size(); i++) {
+ MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
+ mobileSignalController.onMobileDataChanged();
+ }
+ }
+
public boolean isEmergencyOnly() {
if (mMobileSignalControllers.size() == 0) {
// When there are no active subscriptions, determine emengency state from last
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 4fdaa6300065..b5f660a84043 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -235,17 +235,17 @@ public class SmartReplyView extends ViewGroup {
* Add smart actions to be shown next to smart replies. Only the actions that fit into the
* notification are shown.
*/
- public List<Button> inflateSmartActions(@NonNull SmartActions smartActions,
- SmartReplyController smartReplyController, NotificationEntry entry,
- HeadsUpManager headsUpManager, boolean delayOnClickListener) {
+ public List<Button> inflateSmartActions(Context packageContext,
+ @NonNull SmartActions smartActions, SmartReplyController smartReplyController,
+ NotificationEntry entry, HeadsUpManager headsUpManager, boolean delayOnClickListener) {
List<Button> buttons = new ArrayList<>();
int numSmartActions = smartActions.actions.size();
for (int n = 0; n < numSmartActions; n++) {
Notification.Action action = smartActions.actions.get(n);
if (action.actionIntent != null) {
buttons.add(inflateActionButton(
- this, getContext(), n, smartActions, smartReplyController, entry,
- headsUpManager, delayOnClickListener));
+ this, getContext(), packageContext, n, smartActions, smartReplyController,
+ entry, headsUpManager, delayOnClickListener));
}
}
return buttons;
@@ -327,7 +327,7 @@ public class SmartReplyView extends ViewGroup {
@VisibleForTesting
static Button inflateActionButton(SmartReplyView smartReplyView, Context context,
- int actionIndex, SmartActions smartActions,
+ Context packageContext, int actionIndex, SmartActions smartActions,
SmartReplyController smartReplyController, NotificationEntry entry,
HeadsUpManager headsUpManager, boolean useDelayedOnClickListener) {
Notification.Action action = smartActions.actions.get(actionIndex);
@@ -335,7 +335,9 @@ public class SmartReplyView extends ViewGroup {
R.layout.smart_action_button, smartReplyView, false);
button.setText(action.title);
- Drawable iconDrawable = action.getIcon().loadDrawable(context);
+ // We received the Icon from the application - so use the Context of the application to
+ // reference icon resources.
+ Drawable iconDrawable = action.getIcon().loadDrawable(packageContext);
// Add the action icon to the Smart Action button.
int newIconSize = context.getResources().getDimensionPixelSize(
R.dimen.smart_action_button_icon_size);
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
index 97507900e269..453c2f7da71f 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
@@ -31,15 +31,21 @@ import androidx.preference.PreferenceScreen;
import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.SystemUIFactory;
import com.android.systemui.fragments.FragmentService;
+import javax.inject.Inject;
+
public class TunerActivity extends Activity implements
PreferenceFragment.OnPreferenceStartFragmentCallback,
PreferenceFragment.OnPreferenceStartScreenCallback {
private static final String TAG_TUNER = "tuner";
+ @Inject
+ TunerActivity() {
+ super();
+ }
+
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -51,8 +57,6 @@ public class TunerActivity extends Activity implements
setActionBar(toolbar);
}
- Dependency.initDependencies(SystemUIFactory.getInstance().getRootComponent());
-
if (getFragmentManager().findFragmentByTag(TAG_TUNER) == null) {
final String action = getIntent().getAction();
boolean showDemoMode = action != null && action.equals(
@@ -68,7 +72,6 @@ public class TunerActivity extends Activity implements
protected void onDestroy() {
super.onDestroy();
Dependency.destroy(FragmentService.class, s -> s.destroyAll());
- Dependency.clearDependencies();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/util/AsyncSensorManager.java b/packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java
index b9c5ee5a7a7e..dcd0c58a5310 100644
--- a/packages/SystemUI/src/com/android/systemui/util/AsyncSensorManager.java
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.util;
+package com.android.systemui.util.sensors;
import android.content.Context;
import android.hardware.HardwareBuffer;
@@ -56,23 +56,31 @@ public class AsyncSensorManager extends SensorManager
private final SensorManager mInner;
private final List<Sensor> mSensorCache;
- private final HandlerThread mHandlerThread = new HandlerThread("async_sensor");
- @VisibleForTesting final Handler mHandler;
+ private final Handler mHandler;
private final List<SensorManagerPlugin> mPlugins;
@Inject
public AsyncSensorManager(Context context, PluginManager pluginManager) {
- this(context.getSystemService(SensorManager.class), pluginManager);
+ this(context.getSystemService(SensorManager.class), pluginManager, null);
}
@VisibleForTesting
- AsyncSensorManager(SensorManager sensorManager, PluginManager pluginManager) {
+ public AsyncSensorManager(
+ SensorManager sensorManager, PluginManager pluginManager, Handler handler) {
mInner = sensorManager;
- mHandlerThread.start();
- mHandler = new Handler(mHandlerThread.getLooper());
+ if (handler == null) {
+ HandlerThread handlerThread = new HandlerThread("async_sensor");
+ handlerThread.start();
+ mHandler = new Handler(handlerThread.getLooper());
+ } else {
+ mHandler = handler;
+ }
mSensorCache = mInner.getSensorList(Sensor.TYPE_ALL);
mPlugins = new ArrayList<>();
- pluginManager.addPluginListener(this, SensorManagerPlugin.class, true /* allowMultiple */);
+ if (pluginManager != null) {
+ pluginManager.addPluginListener(this, SensorManagerPlugin.class,
+ true /* allowMultiple */);
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
new file mode 100644
index 000000000000..cce5bcadb509
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
@@ -0,0 +1,305 @@
+/*
+ * 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.sensors;
+
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.Handler;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.R;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.function.Consumer;
+
+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 List<ProximitySensorListener> mListeners = new ArrayList<>();
+ private String mTag = null;
+ @VisibleForTesting ProximityEvent mLastEvent;
+ private int mSensorDelay = SensorManager.SENSOR_DELAY_NORMAL;
+ private boolean mPaused;
+ private boolean mRegistered;
+
+ private SensorEventListener mSensorEventListener = new SensorEventListener() {
+ @Override
+ public synchronized void onSensorChanged(SensorEvent event) {
+ onSensorEvent(event);
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ }
+ };
+
+ @Inject
+ public ProximitySensor(Context context, AsyncSensorManager sensorManager) {
+ mSensorManager = sensorManager;
+ Sensor sensor = findBrightnessSensor(context);
+
+ 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;
+ }
+
+ public void setSensorDelay(int sensorDelay) {
+ mSensorDelay = sensorDelay;
+ }
+
+ /**
+ * Unregister with the {@link SensorManager} without unsetting listeners on this object.
+ */
+ public void pause() {
+ mPaused = true;
+ unregisterInternal();
+ }
+
+ /**
+ * Register with the {@link SensorManager}. No-op if no listeners are registered on this object.
+ */
+ public void resume() {
+ mPaused = false;
+ registerInternal();
+ }
+
+ private Sensor findBrightnessSensor(Context context) {
+ String sensorType = context.getString(R.string.doze_brightness_sensor_type);
+ List<Sensor> sensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
+ Sensor sensor = null;
+ for (Sensor s : sensorList) {
+ if (sensorType.equals(s.getStringType())) {
+ sensor = s;
+ break;
+ }
+ }
+
+ return sensor;
+ }
+
+ /**
+ * Returns true if we are registered with the SensorManager.
+ */
+ public boolean isRegistered() {
+ return mRegistered;
+ }
+
+ /**
+ * 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. If a cool down is currently running, the sensor will be registered when it is over.
+ */
+ public boolean register(ProximitySensorListener listener) {
+ if (!getSensorAvailable()) {
+ return false;
+ }
+
+ mListeners.add(listener);
+ registerInternal();
+
+ return true;
+ }
+
+ protected void registerInternal() {
+ if (mRegistered || mPaused || mListeners.isEmpty()) {
+ return;
+ }
+ logDebug("Using brightness sensor? " + mUsingBrightnessSensor);
+ logDebug("Registering sensor listener");
+ mRegistered = true;
+ mSensorManager.registerListener(mSensorEventListener, mSensor, mSensorDelay);
+ }
+
+ /**
+ * 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) {
+ unregisterInternal();
+ }
+ }
+
+ protected void unregisterInternal() {
+ if (!mRegistered) {
+ return;
+ }
+ logDebug("unregistering sensor listener");
+ mSensorManager.unregisterListener(mSensorEventListener);
+ mRegistered = false;
+ }
+
+ public Boolean isNear() {
+ return getSensorAvailable() && mLastEvent != null ? mLastEvent.getNear() : null;
+ }
+
+ /** Update all listeners with the last value this class received from the sensor. */
+ public void alertListeners() {
+ mListeners.forEach(proximitySensorListener ->
+ proximitySensorListener.onSensorEvent(mLastEvent));
+ }
+
+ private void onSensorEvent(SensorEvent event) {
+ boolean near = event.values[0] < mMaxRange;
+ if (mUsingBrightnessSensor) {
+ near = event.values[0] == 0;
+ }
+ mLastEvent = new ProximityEvent(near, event.timestamp);
+ alertListeners();
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{registered=%s, paused=%s, near=%s, sensor=%s}",
+ isRegistered(), mPaused, isNear(), mSensor);
+ }
+
+ /**
+ * Convenience class allowing for briefly checking the proximity sensor.
+ */
+ public static class ProximityCheck implements Runnable {
+
+ private final ProximitySensor mSensor;
+ private final Handler mHandler;
+ private List<Consumer<Boolean>> mCallbacks = new ArrayList<>();
+
+ @Inject
+ public ProximityCheck(ProximitySensor sensor, Handler handler) {
+ mSensor = sensor;
+ mSensor.setTag("prox_check");
+ mHandler = handler;
+ mSensor.pause();
+ ProximitySensorListener listener = proximityEvent -> {
+ mCallbacks.forEach(
+ booleanConsumer ->
+ booleanConsumer.accept(
+ proximityEvent == null ? null : proximityEvent.getNear()));
+ mCallbacks.clear();
+ mSensor.pause();
+ };
+ mSensor.register(listener);
+ }
+
+ /** Set a descriptive tag for the sensors registration. */
+ public void setTag(String tag) {
+ mSensor.setTag(tag);
+ }
+
+ @Override
+ public void run() {
+ mSensor.pause();
+ mSensor.alertListeners();
+ }
+
+ /**
+ * Query the proximity sensor, timing out if no result.
+ */
+ public void check(long timeoutMs, Consumer<Boolean> callback) {
+ if (!mSensor.getSensorAvailable()) {
+ callback.accept(null);
+ }
+ mCallbacks.add(callback);
+ if (!mSensor.isRegistered()) {
+ mSensor.resume();
+ mHandler.postDelayed(this, timeoutMs);
+ }
+ }
+ }
+
+ /** Implement to be notified of ProximityEvents. */
+ public interface ProximitySensorListener {
+ /** Called when the ProximitySensor changes. */
+ void onSensorEvent(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;
+ }
+
+ public long getTimestampMs() {
+ return mTimestampNs / 1000000;
+ }
+
+ @Override
+ public String toString() {
+ return String.format((Locale) null, "{near=%s, timestamp_ns=%d}", mNear, mTimestampNs);
+ }
+
+ }
+
+ private void logDebug(String msg) {
+ if (DEBUG) {
+ Log.d(TAG, (mTag != null ? "[" + mTag + "] " : "") + msg);
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java b/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java
index b9d09ce91c1a..939df10724ca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java
@@ -74,6 +74,9 @@ public class DependencyTest extends SysuiTestCase {
@Test
public void testInitDependency() {
Dependency.clearDependencies();
- Dependency.initDependencies(SystemUIFactory.getInstance().getRootComponent());
+ Dependency dependency = new Dependency();
+ SystemUIFactory
+ .getInstance().getRootComponent().createDependency().createSystemUI(dependency);
+ dependency.start();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
index 9aeb147cc725..5c4ef18b61a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
@@ -393,8 +393,12 @@ 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) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceNotificationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceNotificationListenerTest.java
index 5943b02b4e4a..212c93dc576d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceNotificationListenerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceNotificationListenerTest.java
@@ -17,19 +17,17 @@
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 static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
import android.app.Notification;
-import android.service.notification.NotificationListenerService.Ranking;
-import android.service.notification.StatusBarNotification;
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;
@@ -40,7 +38,6 @@ import org.junit.runner.RunWith;
@SmallTest
public class ForegroundServiceNotificationListenerTest extends SysuiTestCase {
private ForegroundServiceLifetimeExtender mExtender = new ForegroundServiceLifetimeExtender();
- private StatusBarNotification mSbn;
private NotificationEntry mEntry;
private Notification mNotif;
@@ -52,10 +49,9 @@ public class ForegroundServiceNotificationListenerTest extends SysuiTestCase {
.setContentText("Text")
.build();
- mSbn = mock(StatusBarNotification.class);
- when(mSbn.getNotification()).thenReturn(mNotif);
-
- mEntry = new NotificationEntry(mSbn, mock(Ranking.class));
+ mEntry = new NotificationEntryBuilder()
+ .setNotification(mNotif)
+ .build();
}
/**
@@ -66,21 +62,27 @@ public class ForegroundServiceNotificationListenerTest extends SysuiTestCase {
// Extend the lifetime of a FGS notification iff it has not been visible
// for the minimum time
mNotif.flags |= Notification.FLAG_FOREGROUND_SERVICE;
- when(mSbn.getPostTime()).thenReturn(System.currentTimeMillis());
+ modifySbn(mEntry)
+ .setPostTime(System.currentTimeMillis())
+ .build();
assertTrue(mExtender.shouldExtendLifetime(mEntry));
}
@Test
public void testShouldExtendLifetime_shouldNot_foreground() {
mNotif.flags |= Notification.FLAG_FOREGROUND_SERVICE;
- when(mSbn.getPostTime()).thenReturn(System.currentTimeMillis() - MIN_FGS_TIME_MS - 1);
+ modifySbn(mEntry)
+ .setPostTime(System.currentTimeMillis() - MIN_FGS_TIME_MS - 1)
+ .build();
assertFalse(mExtender.shouldExtendLifetime(mEntry));
}
@Test
public void testShouldExtendLifetime_shouldNot_notForeground() {
mNotif.flags = 0;
- when(mSbn.getPostTime()).thenReturn(System.currentTimeMillis() - MIN_FGS_TIME_MS - 1);
+ 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/AssistHandleLikeHomeBehaviorTest.java b/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleLikeHomeBehaviorTest.java
index c53289c62d94..fe131275afd8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleLikeHomeBehaviorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleLikeHomeBehaviorTest.java
@@ -31,6 +31,7 @@ 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.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.system.QuickStepContract;
import org.junit.Before;
@@ -47,6 +48,7 @@ public class AssistHandleLikeHomeBehaviorTest extends SysuiTestCase {
private AssistHandleLikeHomeBehavior mAssistHandleLikeHomeBehavior;
+ @Mock private StatusBarStateController mMockStatusBarStateController;
@Mock private WakefulnessLifecycle mMockWakefulnessLifecycle;
@Mock private SysUiState mMockSysUiState;
@Mock private AssistHandleCallbacks mMockAssistHandleCallbacks;
@@ -55,7 +57,9 @@ public class AssistHandleLikeHomeBehaviorTest extends SysuiTestCase {
public void setup() {
MockitoAnnotations.initMocks(this);
mAssistHandleLikeHomeBehavior = new AssistHandleLikeHomeBehavior(
- () -> mMockWakefulnessLifecycle, () -> mMockSysUiState);
+ () -> mMockStatusBarStateController,
+ () -> mMockWakefulnessLifecycle,
+ () -> mMockSysUiState);
}
@Test
@@ -66,6 +70,9 @@ public class AssistHandleLikeHomeBehaviorTest extends SysuiTestCase {
mAssistHandleLikeHomeBehavior.onModeActivated(mContext, mMockAssistHandleCallbacks);
// Assert
+ verify(mMockStatusBarStateController).isDozing();
+ verify(mMockStatusBarStateController).addCallback(
+ any(StatusBarStateController.StateListener.class));
verify(mMockWakefulnessLifecycle).getWakefulness();
verify(mMockWakefulnessLifecycle).addObserver(any(WakefulnessLifecycle.Observer.class));
verify(mMockSysUiState).addCallback(any(SysUiState.SysUiStateCallback.class));
@@ -73,8 +80,9 @@ public class AssistHandleLikeHomeBehaviorTest extends SysuiTestCase {
}
@Test
- public void onModeActivated_showsHandlesWhenAwake() {
+ public void onModeActivated_showsHandlesWhenFullyAwake() {
// Arrange
+ when(mMockStatusBarStateController.isDozing()).thenReturn(false);
when(mMockWakefulnessLifecycle.getWakefulness())
.thenReturn(WakefulnessLifecycle.WAKEFULNESS_AWAKE);
@@ -89,6 +97,7 @@ public class AssistHandleLikeHomeBehaviorTest extends SysuiTestCase {
@Test
public void onModeActivated_hidesHandlesWhenNotAwake() {
// Arrange
+ when(mMockStatusBarStateController.isDozing()).thenReturn(true);
when(mMockWakefulnessLifecycle.getWakefulness())
.thenReturn(WakefulnessLifecycle.WAKEFULNESS_ASLEEP);
@@ -101,72 +110,139 @@ public class AssistHandleLikeHomeBehaviorTest extends SysuiTestCase {
}
@Test
+ public void onModeActivated_hidesHandlesWhenDozing() {
+ // Arrange
+ when(mMockStatusBarStateController.isDozing()).thenReturn(true);
+ when(mMockWakefulnessLifecycle.getWakefulness())
+ .thenReturn(WakefulnessLifecycle.WAKEFULNESS_AWAKE);
+
+ // Act
+ mAssistHandleLikeHomeBehavior.onModeActivated(mContext, mMockAssistHandleCallbacks);
+
+ // Assert
+ verify(mMockAssistHandleCallbacks).hide();
+ verifyNoMoreInteractions(mMockAssistHandleCallbacks);
+ }
+
+ @Test
public void onModeDeactivated_stopsObserving() {
// Arrange
mAssistHandleLikeHomeBehavior.onModeActivated(mContext, mMockAssistHandleCallbacks);
+ ArgumentCaptor<StatusBarStateController.StateListener> stateListener =
+ ArgumentCaptor.forClass(StatusBarStateController.StateListener.class);
ArgumentCaptor<WakefulnessLifecycle.Observer> observer =
ArgumentCaptor.forClass(WakefulnessLifecycle.Observer.class);
ArgumentCaptor<SysUiState.SysUiStateCallback> sysUiStateCallback =
ArgumentCaptor.forClass(SysUiState.SysUiStateCallback.class);
+ verify(mMockStatusBarStateController).addCallback(stateListener.capture());
verify(mMockWakefulnessLifecycle).addObserver(observer.capture());
verify(mMockSysUiState).addCallback(sysUiStateCallback.capture());
- reset(mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+ reset(
+ mMockStatusBarStateController,
+ mMockWakefulnessLifecycle,
+ mMockSysUiState,
+ mMockAssistHandleCallbacks);
// Act
mAssistHandleLikeHomeBehavior.onModeDeactivated();
// Assert
+ verify(mMockStatusBarStateController).removeCallback(eq(stateListener.getValue()));
verify(mMockWakefulnessLifecycle).removeObserver(eq(observer.getValue()));
verify(mMockSysUiState).removeCallback(eq(sysUiStateCallback.getValue()));
verifyNoMoreInteractions(
- mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+ mMockStatusBarStateController,
+ mMockWakefulnessLifecycle,
+ mMockSysUiState,
+ mMockAssistHandleCallbacks);
}
@Test
public void onAssistantGesturePerformed_doesNothing() {
// Arrange
mAssistHandleLikeHomeBehavior.onModeActivated(mContext, mMockAssistHandleCallbacks);
- reset(mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+ reset(
+ mMockStatusBarStateController,
+ mMockWakefulnessLifecycle,
+ mMockSysUiState,
+ mMockAssistHandleCallbacks);
// Act
mAssistHandleLikeHomeBehavior.onAssistantGesturePerformed();
// Assert
verifyNoMoreInteractions(
- mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+ mMockStatusBarStateController,
+ mMockWakefulnessLifecycle,
+ mMockSysUiState,
+ mMockAssistHandleCallbacks);
}
@Test
public void onAssistHandlesRequested_doesNothing() {
// Arrange
mAssistHandleLikeHomeBehavior.onModeActivated(mContext, mMockAssistHandleCallbacks);
- reset(mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+ reset(
+ mMockStatusBarStateController,
+ mMockWakefulnessLifecycle,
+ mMockSysUiState,
+ mMockAssistHandleCallbacks);
// Act
mAssistHandleLikeHomeBehavior.onAssistHandlesRequested();
// Assert
verifyNoMoreInteractions(
- mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+ mMockStatusBarStateController,
+ mMockWakefulnessLifecycle,
+ mMockSysUiState,
+ mMockAssistHandleCallbacks);
}
@Test
- public void onWake_handlesShow() {
+ public void onBothAwakeAndUnDoze_handlesShow() {
// Arrange
+ when(mMockStatusBarStateController.isDozing()).thenReturn(true);
when(mMockWakefulnessLifecycle.getWakefulness())
.thenReturn(WakefulnessLifecycle.WAKEFULNESS_ASLEEP);
+ ArgumentCaptor<StatusBarStateController.StateListener> stateListener =
+ ArgumentCaptor.forClass(StatusBarStateController.StateListener.class);
ArgumentCaptor<WakefulnessLifecycle.Observer> observer =
ArgumentCaptor.forClass(WakefulnessLifecycle.Observer.class);
mAssistHandleLikeHomeBehavior.onModeActivated(mContext, mMockAssistHandleCallbacks);
+ verify(mMockStatusBarStateController).addCallback(stateListener.capture());
verify(mMockWakefulnessLifecycle).addObserver(observer.capture());
- reset(mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+ reset(
+ mMockStatusBarStateController,
+ mMockWakefulnessLifecycle,
+ mMockSysUiState,
+ mMockAssistHandleCallbacks);
// Act
- observer.getValue().onStartedWakingUp();
+ observer.getValue().onFinishedWakingUp();
// Assert
+ verify(mMockAssistHandleCallbacks).hide();
verifyNoMoreInteractions(
- mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+ mMockStatusBarStateController,
+ mMockWakefulnessLifecycle,
+ mMockSysUiState,
+ mMockAssistHandleCallbacks);
+
+ // Arrange
+ observer.getValue().onFinishedGoingToSleep();
+ reset(mMockAssistHandleCallbacks);
+
+ // Act
+ stateListener.getValue().onDozingChanged(false);
+
+ // Assert
+ verify(mMockAssistHandleCallbacks).hide();
+ verifyNoMoreInteractions(
+ mMockStatusBarStateController,
+ mMockWakefulnessLifecycle,
+ mMockSysUiState,
+ mMockAssistHandleCallbacks);
// Act
observer.getValue().onFinishedWakingUp();
@@ -174,19 +250,30 @@ public class AssistHandleLikeHomeBehaviorTest extends SysuiTestCase {
// Assert
verify(mMockAssistHandleCallbacks).showAndStay();
verifyNoMoreInteractions(
- mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+ mMockStatusBarStateController,
+ mMockWakefulnessLifecycle,
+ mMockSysUiState,
+ mMockAssistHandleCallbacks);
}
@Test
- public void onSleep_handlesHide() {
+ public void onSleepOrDoze_handlesHide() {
// Arrange
+ when(mMockStatusBarStateController.isDozing()).thenReturn(false);
when(mMockWakefulnessLifecycle.getWakefulness())
.thenReturn(WakefulnessLifecycle.WAKEFULNESS_AWAKE);
+ ArgumentCaptor<StatusBarStateController.StateListener> stateListener =
+ ArgumentCaptor.forClass(StatusBarStateController.StateListener.class);
ArgumentCaptor<WakefulnessLifecycle.Observer> observer =
ArgumentCaptor.forClass(WakefulnessLifecycle.Observer.class);
mAssistHandleLikeHomeBehavior.onModeActivated(mContext, mMockAssistHandleCallbacks);
+ verify(mMockStatusBarStateController).addCallback(stateListener.capture());
verify(mMockWakefulnessLifecycle).addObserver(observer.capture());
- reset(mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+ reset(
+ mMockStatusBarStateController,
+ mMockWakefulnessLifecycle,
+ mMockSysUiState,
+ mMockAssistHandleCallbacks);
// Act
observer.getValue().onStartedGoingToSleep();
@@ -194,26 +281,42 @@ public class AssistHandleLikeHomeBehaviorTest extends SysuiTestCase {
// Assert
verify(mMockAssistHandleCallbacks).hide();
verifyNoMoreInteractions(
- mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+ mMockStatusBarStateController,
+ mMockWakefulnessLifecycle,
+ mMockSysUiState,
+ mMockAssistHandleCallbacks);
+
+ // Arrange
+ observer.getValue().onFinishedWakingUp();
+ reset(mMockAssistHandleCallbacks);
// Act
- observer.getValue().onFinishedGoingToSleep();
+ stateListener.getValue().onDozingChanged(true);
// Assert
+ verify(mMockAssistHandleCallbacks).hide();
verifyNoMoreInteractions(
- mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+ mMockStatusBarStateController,
+ mMockWakefulnessLifecycle,
+ mMockSysUiState,
+ mMockAssistHandleCallbacks);
}
@Test
public void onHomeHandleHide_handlesHide() {
// Arrange
+ when(mMockStatusBarStateController.isDozing()).thenReturn(false);
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);
+ reset(
+ mMockStatusBarStateController,
+ mMockWakefulnessLifecycle,
+ mMockSysUiState,
+ mMockAssistHandleCallbacks);
// Act
sysUiStateCallback.getValue().onSystemUiStateChanged(
@@ -222,12 +325,16 @@ public class AssistHandleLikeHomeBehaviorTest extends SysuiTestCase {
// Assert
verify(mMockAssistHandleCallbacks).hide();
verifyNoMoreInteractions(
- mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+ mMockStatusBarStateController,
+ mMockWakefulnessLifecycle,
+ mMockSysUiState,
+ mMockAssistHandleCallbacks);
}
@Test
public void onHomeHandleUnhide_handlesShow() {
// Arrange
+ when(mMockStatusBarStateController.isDozing()).thenReturn(false);
when(mMockWakefulnessLifecycle.getWakefulness())
.thenReturn(WakefulnessLifecycle.WAKEFULNESS_AWAKE);
ArgumentCaptor<SysUiState.SysUiStateCallback> sysUiStateCallback =
@@ -236,7 +343,11 @@ public class AssistHandleLikeHomeBehaviorTest extends SysuiTestCase {
verify(mMockSysUiState).addCallback(sysUiStateCallback.capture());
sysUiStateCallback.getValue().onSystemUiStateChanged(
QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN);
- reset(mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+ reset(
+ mMockStatusBarStateController,
+ mMockWakefulnessLifecycle,
+ mMockSysUiState,
+ mMockAssistHandleCallbacks);
// Act
sysUiStateCallback.getValue().onSystemUiStateChanged(
@@ -245,6 +356,9 @@ public class AssistHandleLikeHomeBehaviorTest extends SysuiTestCase {
// Assert
verify(mMockAssistHandleCallbacks).showAndStay();
verifyNoMoreInteractions(
- mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+ mMockStatusBarStateController,
+ mMockWakefulnessLifecycle,
+ mMockSysUiState,
+ mMockAssistHandleCallbacks);
}
}
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..3561e3465898 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.sensors.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..35d59c1c9726 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.sensors.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/doze/DozeConfigurationUtil.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
index 0c124fff53a3..752e145d79bd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
@@ -24,7 +24,7 @@ import static org.mockito.Mockito.when;
import static org.mockito.Mockito.withSettings;
import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.utils.hardware.FakeSensorManager;
+import com.android.systemui.util.sensors.FakeSensorManager;
import org.mockito.Answers;
import org.mockito.MockSettings;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
index 392c677b9827..aa62e9aca4fe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
@@ -40,7 +40,7 @@ import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.utils.hardware.FakeSensorManager;
+import com.android.systemui.util.sensors.FakeSensorManager;
import org.junit.Before;
import org.junit.Test;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
index cd6d1e069566..ddd1685bf8bc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
@@ -44,7 +44,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.doze.DozeSensors.TriggerSensor;
import com.android.systemui.plugins.SensorManagerPlugin;
import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.util.AsyncSensorManager;
+import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.wakelock.WakeLock;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index e190f9923da8..b0e3969bf8a4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -32,6 +32,7 @@ import android.hardware.display.AmbientDisplayConfiguration;
import android.os.Handler;
import android.os.Looper;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import androidx.test.filters.SmallTest;
@@ -39,9 +40,12 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dock.DockManager;
import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.util.sensors.AsyncSensorManager;
+import com.android.systemui.util.sensors.FakeProximitySensor;
+import com.android.systemui.util.sensors.FakeSensorManager;
+import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.wakelock.WakeLock;
import com.android.systemui.util.wakelock.WakeLockFake;
-import com.android.systemui.utils.hardware.FakeSensorManager;
import org.junit.Before;
import org.junit.BeforeClass;
@@ -55,13 +59,10 @@ public class DozeTriggersTest extends SysuiTestCase {
private DozeTriggers mTriggers;
private DozeMachine mMachine;
private DozeHostFake mHost;
- private AmbientDisplayConfiguration mConfig;
- private DozeParameters mParameters;
private FakeSensorManager mSensors;
private Sensor mTapSensor;
- private WakeLock mWakeLock;
- private AlarmManager mAlarmManager;
private DockManager mDockManagerFake;
+ private FakeProximitySensor mProximitySensor;
@BeforeClass
public static void setupSuite() {
@@ -72,18 +73,22 @@ public class DozeTriggersTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
mMachine = mock(DozeMachine.class);
- mAlarmManager = mock(AlarmManager.class);
+ AlarmManager alarmManager = mock(AlarmManager.class);
mHost = spy(new DozeHostFake());
- mConfig = DozeConfigurationUtil.createMockConfig();
- mParameters = DozeConfigurationUtil.createMockParameters();
+ AmbientDisplayConfiguration config = DozeConfigurationUtil.createMockConfig();
+ DozeParameters parameters = DozeConfigurationUtil.createMockParameters();
mSensors = spy(new FakeSensorManager(mContext));
mTapSensor = mSensors.getFakeTapSensor().getSensor();
- mWakeLock = new WakeLockFake();
+ WakeLock wakeLock = new WakeLockFake();
mDockManagerFake = mock(DockManager.class);
-
- mTriggers = new DozeTriggers(mContext, mMachine, mHost, mAlarmManager, mConfig, mParameters,
- mSensors, Handler.createAsync(Looper.myLooper()), mWakeLock, true,
- mDockManagerFake);
+ AsyncSensorManager asyncSensorManager =
+ new AsyncSensorManager(mSensors, null, new Handler());
+ mProximitySensor = new FakeProximitySensor(getContext(), asyncSensorManager);
+
+ mTriggers = new DozeTriggers(mContext, mMachine, mHost, alarmManager, config, parameters,
+ asyncSensorManager, Handler.createAsync(Looper.myLooper()), wakeLock, true,
+ mDockManagerFake, mProximitySensor);
+ waitForSensorManager();
}
@Test
@@ -94,14 +99,17 @@ public class DozeTriggersTest extends SysuiTestCase {
mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE);
clearInvocations(mMachine);
+ mProximitySensor.setLastEvent(new ProximitySensor.ProximityEvent(true, 1));
mHost.callback.onNotificationAlerted(null /* pulseSuppressedListener */);
- mSensors.getMockProximitySensor().sendProximityResult(false); /* Near */
+ mProximitySensor.alertListeners();
verify(mMachine, never()).requestState(any());
verify(mMachine, never()).requestPulse(anyInt());
mHost.callback.onNotificationAlerted(null /* pulseSuppressedListener */);
- mSensors.getMockProximitySensor().sendProximityResult(true); /* Far */
+ waitForSensorManager();
+ mProximitySensor.setLastEvent(new ProximitySensor.ProximityEvent(false, 2));
+ mProximitySensor.alertListeners();
verify(mMachine).requestPulse(anyInt());
}
@@ -111,6 +119,7 @@ public class DozeTriggersTest extends SysuiTestCase {
when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE);
mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE);
+ waitForSensorManager();
verify(mSensors).requestTriggerSensor(any(), eq(mTapSensor));
clearInvocations(mSensors);
@@ -118,10 +127,12 @@ public class DozeTriggersTest extends SysuiTestCase {
DozeMachine.State.DOZE_REQUEST_PULSE);
mTriggers.transitionTo(DozeMachine.State.DOZE_REQUEST_PULSE,
DozeMachine.State.DOZE_PULSING);
+ waitForSensorManager();
verify(mSensors).cancelTriggerSensor(any(), eq(mTapSensor));
clearInvocations(mSensors);
mTriggers.transitionTo(DozeMachine.State.DOZE_PULSING, DozeMachine.State.DOZE_PULSE_DONE);
+ waitForSensorManager();
verify(mSensors).requestTriggerSensor(any(), eq(mTapSensor));
}
@@ -133,4 +144,16 @@ public class DozeTriggersTest extends SysuiTestCase {
mTriggers.transitionTo(DozeMachine.State.DOZE, DozeMachine.State.FINISH);
verify(mDockManagerFake).removeListener(any());
}
+
+ @Test
+ public void testProximitySensorNotAvailablel() {
+ mProximitySensor.setSensorAvailable(false);
+ mTriggers.onSensor(DozeLog.PULSE_REASON_SENSOR_LONG_PRESS, 100, 100, null);
+ mTriggers.onSensor(DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN, 100, 100, new float[]{1});
+ mTriggers.onSensor(DozeLog.REASON_SENSOR_TAP, 100, 100, null);
+ }
+
+ private void waitForSensorManager() {
+ TestableLooper.get(this).processAllMessages();
+ }
}
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
index ded3ba592abe..fcfdd11a1906 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryBuilder.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryBuilder.java
@@ -38,7 +38,6 @@ import java.util.ArrayList;
public class NotificationEntryBuilder {
private final SbnBuilder mSbnBuilder = new SbnBuilder();
private final RankingBuilder mRankingBuilder = new RankingBuilder();
-
private StatusBarNotification mSbn = null;
public NotificationEntry build() {
@@ -193,8 +192,18 @@ public class NotificationEntryBuilder {
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
index b5168c6452d1..33b0d2c32ba1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryHelper.java
@@ -17,6 +17,7 @@
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;
@@ -27,6 +28,10 @@ public class NotificationEntryHelper {
return new ModifiedRankingBuilder(entry);
}
+ public static ModifiedSbnBuilder modifySbn(NotificationEntry entry) {
+ return new ModifiedSbnBuilder(entry);
+ }
+
public static class ModifiedRankingBuilder extends RankingBuilder {
private final NotificationEntry mTarget;
@@ -42,4 +47,20 @@ public class NotificationEntryHelper {
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/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 b1e76120b752..de77af8f4d14 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -299,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(),
@@ -317,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/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 b45077ade3e3..820f4652e685 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java
@@ -24,6 +24,7 @@ import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.SnoozeCriterion;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
/**
@@ -50,6 +51,7 @@ public class RankingBuilder {
private ArrayList<Notification.Action> mSmartActions = new ArrayList<>();
private ArrayList<CharSequence> mSmartReplies = new ArrayList<>();
private boolean mCanBubble = false;
+ private boolean mIsVisuallyInterruptive = false;
public RankingBuilder() {
}
@@ -97,7 +99,8 @@ public class RankingBuilder {
mNoisy,
mSmartActions,
mSmartReplies,
- mCanBubble);
+ mCanBubble,
+ mIsVisuallyInterruptive);
return ranking;
}
@@ -192,11 +195,21 @@ public class RankingBuilder {
return this;
}
+ 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 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;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SbnBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SbnBuilder.java
index a9aab7f56cd3..9bc962c77019 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SbnBuilder.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SbnBuilder.java
@@ -37,6 +37,22 @@ public class SbnBuilder {
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,
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 53d6bffdc896..30e02e6b46d2 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
@@ -183,7 +183,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
0,
NotificationManager.IMPORTANCE_DEFAULT,
null, null,
- null, null, null, true, sentiment, false, -1, false, null, null, false);
+ null, null, null, true, sentiment, false, -1, false, null, null, false, false);
return true;
}).when(mRankingMap).getRanking(eq(key), any(NotificationListenerService.Ranking.class));
}
@@ -202,7 +202,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
null, null,
null, null, null, true,
NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL, false, -1,
- false, smartActions, null, false);
+ false, smartActions, null, false, false);
return true;
}).when(mRankingMap).getRanking(eq(key), any(NotificationListenerService.Ranking.class));
}
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 365b80b50723..657ec61dfd11 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,58 +295,65 @@ 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.getSmartActions());
assertEquals(NOTIFICATION_CHANNEL, entry.getChannel());
@@ -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";
@@ -623,6 +653,7 @@ public class NotificationDataTest extends SysuiTestCase {
public static final String OVERRIDE_SMART_ACTIONS = "sa";
public static final String OVERRIDE_SMART_REPLIES = "sr";
public static final String OVERRIDE_BUBBLE = "cb";
+ public static final String OVERRIDE_VISUALLY_INTERRUPTIVE = "vi";
public Map<String, Bundle> rankingOverrides = new HashMap<>();
@@ -683,7 +714,14 @@ public class NotificationDataTest extends SysuiTestCase {
overrides.containsKey(OVERRIDE_SMART_REPLIES)
? overrides.getCharSequenceArrayList(OVERRIDE_SMART_REPLIES)
: currentReplies,
- overrides.getBoolean(OVERRIDE_BUBBLE, outRanking.canBubble()));
+ overrides.getBoolean(OVERRIDE_BUBBLE, outRanking.canBubble()),
+ overrides.getBoolean(OVERRIDE_VISUALLY_INTERRUPTIVE,
+ outRanking.visuallyInterruptive()));
+ } else {
+ outRanking.populate(
+ new RankingBuilder()
+ .setKey(key)
+ .build());
}
return true;
}
@@ -704,4 +742,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/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/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index 2f2449473e1b..219aef1ec685 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -131,7 +131,7 @@ public class NotificationPanelViewTest extends SysuiTestCase {
mKeyguardBypassController);
PulseExpansionHandler expansionHandler = new PulseExpansionHandler(mContext, coordinator,
mKeyguardBypassController, mHeadsUpManager,
- mock(NotificationRoundnessManager.class));
+ mock(NotificationRoundnessManager.class), mStatusBarStateController);
mNotificationPanelView = new TestableNotificationPanelView(coordinator, expansionHandler,
mKeyguardBypassController);
mNotificationPanelView.setHeadsUpManager(mHeadsUpManager);
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/policy/BluetoothControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
index 766ad978f475..e629a4f03586 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
@@ -27,7 +27,6 @@ import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
-import android.os.Looper;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
@@ -78,6 +77,7 @@ public class BluetoothControllerImplTest extends SysuiTestCase {
mBluetoothControllerImpl = new BluetoothControllerImpl(mContext,
mTestableLooper.getLooper(),
+ mTestableLooper.getLooper(),
mMockBluetoothManager);
}
@@ -109,18 +109,13 @@ public class BluetoothControllerImplTest extends SysuiTestCase {
BluetoothController.Callback callback = mock(BluetoothController.Callback.class);
mBluetoothControllerImpl.addCallback(callback);
- // Grab the main looper, we'll need it later.
- TestableLooper mainLooper = new TestableLooper(Looper.getMainLooper());
-
// Trigger the state getting.
assertEquals(BluetoothDevice.BOND_NONE, mBluetoothControllerImpl.getBondState(device));
- mTestableLooper.processMessages(1);
- mainLooper.processAllMessages();
+ mTestableLooper.processAllMessages();
assertEquals(BluetoothDevice.BOND_BONDED, mBluetoothControllerImpl.getBondState(device));
verify(callback).onBluetoothDevicesChanged();
- mainLooper.destroy();
}
@Test
@@ -130,20 +125,15 @@ public class BluetoothControllerImplTest extends SysuiTestCase {
BluetoothController.Callback callback = mock(BluetoothController.Callback.class);
mBluetoothControllerImpl.addCallback(callback);
- // Grab the main looper, we'll need it later.
- TestableLooper mainLooper = new TestableLooper(Looper.getMainLooper());
-
// Trigger the state getting.
assertEquals(BluetoothProfile.STATE_DISCONNECTED,
mBluetoothControllerImpl.getMaxConnectionState(device));
- mTestableLooper.processMessages(1);
- mainLooper.processAllMessages();
+ mTestableLooper.processAllMessages();
assertEquals(BluetoothProfile.STATE_CONNECTED,
mBluetoothControllerImpl.getMaxConnectionState(device));
verify(callback).onBluetoothDevicesChanged();
- mainLooper.destroy();
}
@Test
@@ -153,19 +143,11 @@ public class BluetoothControllerImplTest extends SysuiTestCase {
BluetoothController.Callback callback = mock(BluetoothController.Callback.class);
mBluetoothControllerImpl.addCallback(callback);
- // Grab the main looper, we'll need it later.
- TestableLooper mainLooper = new TestableLooper(Looper.getMainLooper());
-
- try {
- // Trigger the state getting.
- assertEquals(BluetoothProfile.STATE_DISCONNECTED,
- mBluetoothControllerImpl.getMaxConnectionState(null));
+ // Trigger the state getting.
+ assertEquals(BluetoothProfile.STATE_DISCONNECTED,
+ mBluetoothControllerImpl.getMaxConnectionState(null));
- mTestableLooper.processMessages(1);
- mainLooper.processAllMessages();
- } finally {
- mainLooper.destroy();
- }
+ mTestableLooper.processAllMessages();
}
@Test
@@ -217,15 +199,8 @@ public class BluetoothControllerImplTest extends SysuiTestCase {
mBluetoothControllerImpl.onAclConnectionStateChanged(device,
BluetoothProfile.STATE_CONNECTED);
- // Grab the main looper, we'll need it later.
- TestableLooper mainLooper = new TestableLooper(Looper.getMainLooper());
+ mTestableLooper.processAllMessages();
- try {
- mTestableLooper.processAllMessages();
- mainLooper.processAllMessages();
- } finally {
- mainLooper.destroy();
- }
assertTrue(mBluetoothControllerImpl.isBluetoothConnected());
verify(callback, atLeastOnce()).onBluetoothStateChange(anyBoolean());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
index 3ddfbdac6db8..aa4723acba62 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
@@ -147,7 +147,7 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest {
}
@Test
- public void testNoInternetIcon_withoutDefaultSub() {
+ public void testNonDefaultSIM_showsFullSignal_connected() {
setupNetworkController();
when(mMockTm.isDataCapable()).thenReturn(false);
setupDefaultSignal();
@@ -158,11 +158,11 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest {
// Verify that a SignalDrawable with a cut out is used to display data disabled.
verifyLastMobileDataIndicators(true, DEFAULT_SIGNAL_STRENGTH, 0,
true, DEFAULT_QS_SIGNAL_STRENGTH, 0, false,
- false, true, NOT_DEFAULT_DATA_STRING);
+ false, false, NOT_DEFAULT_DATA_STRING);
}
@Test
- public void testDataDisabledIcon_withoutDefaultSub() {
+ public void testNonDefaultSIM_showsFullSignal_disconnected() {
setupNetworkController();
when(mMockTm.isDataCapable()).thenReturn(false);
setupDefaultSignal();
@@ -173,7 +173,7 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest {
// Verify that a SignalDrawable with a cut out is used to display data disabled.
verifyLastMobileDataIndicators(true, DEFAULT_SIGNAL_STRENGTH, 0,
true, DEFAULT_QS_SIGNAL_STRENGTH, 0, false,
- false, true, NOT_DEFAULT_DATA_STRING);
+ false, false, NOT_DEFAULT_DATA_STRING);
}
@Test
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 9831ce3c07ce..0d56cbe84eb0 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;
@@ -140,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);
}
@@ -501,6 +500,7 @@ public class SmartReplyViewTest extends SysuiTestCase {
private void setSmartActions(String[] actionTitles, boolean useDelayedOnClickListener) {
mView.resetSmartSuggestions(mContainer);
List<Button> actions = mView.inflateSmartActions(
+ getContext(),
new SmartReplyView.SmartActions(createActions(actionTitles), false),
mLogger,
mEntry,
@@ -521,6 +521,7 @@ public class SmartReplyViewTest extends SysuiTestCase {
List<Button> smartSuggestions = inflateSmartReplies(choices, fromAssistant,
useDelayedOnClickListener);
smartSuggestions.addAll(mView.inflateSmartActions(
+ getContext(),
new SmartReplyView.SmartActions(createActions(actionTitles), fromAssistant),
mLogger,
mEntry,
@@ -867,7 +868,7 @@ public class SmartReplyViewTest extends SysuiTestCase {
}
private Button inflateActionButton(Notification.Action action) {
- return SmartReplyView.inflateActionButton(mView, getContext(), 0,
+ return SmartReplyView.inflateActionButton(mView, getContext(), getContext(), 0,
new SmartReplyView.SmartActions(Collections.singletonList(action), false),
mLogger, mEntry, mHeadsUpManager, true /* useDelayedOnClickListener */);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/AsyncSensorManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/AsyncSensorManagerTest.java
index 4a9b1b3c4006..9149599f2c7c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/AsyncSensorManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/AsyncSensorManagerTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 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.
@@ -14,9 +14,8 @@
* limitations under the License.
*/
-package com.android.systemui.util;
+package com.android.systemui.util.sensors;
-import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
@@ -24,15 +23,15 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.hardware.SensorEventListener;
-import android.hardware.SensorManager;
+import android.os.Handler;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.SensorManagerPlugin;
import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.utils.hardware.FakeSensorManager;
import org.junit.Before;
import org.junit.Test;
@@ -40,20 +39,21 @@ import org.junit.runner.RunWith;
@SmallTest
@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
public class AsyncSensorManagerTest extends SysuiTestCase {
- private TestableAsyncSensorManager mAsyncSensorManager;
- private FakeSensorManager mFakeSensorManager;
+ private AsyncSensorManager mAsyncSensorManager;
private SensorEventListener mListener;
- private FakeSensorManager.MockProximitySensor mSensor;
+ private FakeSensorManager.FakeProximitySensor mSensor;
private PluginManager mPluginManager;
@Before
public void setUp() throws Exception {
mPluginManager = mock(PluginManager.class);
- mFakeSensorManager = new FakeSensorManager(mContext);
- mAsyncSensorManager = new TestableAsyncSensorManager(mFakeSensorManager);
- mSensor = mFakeSensorManager.getMockProximitySensor();
+ FakeSensorManager fakeSensorManager = new FakeSensorManager(mContext);
+ mAsyncSensorManager = new AsyncSensorManager(
+ fakeSensorManager, mPluginManager, new Handler());
+ mSensor = fakeSensorManager.getFakeProximitySensor();
mListener = mock(SensorEventListener.class);
}
@@ -61,7 +61,7 @@ public class AsyncSensorManagerTest extends SysuiTestCase {
public void registerListenerImpl() throws Exception {
mAsyncSensorManager.registerListener(mListener, mSensor.getSensor(), 100);
- mAsyncSensorManager.waitUntilRequestsCompleted();
+ waitUntilRequestsCompleted();
// Verify listener was registered.
mSensor.sendProximityResult(true);
@@ -73,7 +73,7 @@ public class AsyncSensorManagerTest extends SysuiTestCase {
mAsyncSensorManager.registerListener(mListener, mSensor.getSensor(), 100);
mAsyncSensorManager.unregisterListener(mListener);
- mAsyncSensorManager.waitUntilRequestsCompleted();
+ waitUntilRequestsCompleted();
// Verify listener was unregistered.
mSensor.sendProximityResult(true);
@@ -85,7 +85,7 @@ public class AsyncSensorManagerTest extends SysuiTestCase {
mAsyncSensorManager.registerListener(mListener, mSensor.getSensor(), 100);
mAsyncSensorManager.unregisterListener(mListener, mSensor.getSensor());
- mAsyncSensorManager.waitUntilRequestsCompleted();
+ waitUntilRequestsCompleted();
// Verify listener was unregistered.
mSensor.sendProximityResult(true);
@@ -98,13 +98,7 @@ public class AsyncSensorManagerTest extends SysuiTestCase {
eq(SensorManagerPlugin.class), eq(true) /* allowMultiple */);
}
- private class TestableAsyncSensorManager extends AsyncSensorManager {
- public TestableAsyncSensorManager(SensorManager sensorManager) {
- super(sensorManager, mPluginManager);
- }
-
- public void waitUntilRequestsCompleted() {
- assertTrue(mHandler.runWithScissors(() -> {}, 0));
- }
+ public void waitUntilRequestsCompleted() {
+ TestableLooper.get(this).processAllMessages();
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java
new file mode 100644
index 000000000000..d7df96dfdf24
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java
@@ -0,0 +1,53 @@
+/*
+ * 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.sensors;
+
+import android.content.Context;
+
+public class FakeProximitySensor extends ProximitySensor {
+ private boolean mAvailable;
+ private boolean mPaused;
+
+ public FakeProximitySensor(Context context, AsyncSensorManager sensorManager) {
+ super(context, sensorManager);
+
+ mAvailable = true;
+ }
+
+ public void setSensorAvailable(boolean available) {
+ mAvailable = available;
+ }
+
+ public void setLastEvent(ProximityEvent event) {
+ mLastEvent = event;
+ }
+
+ @Override
+ public boolean getSensorAvailable() {
+ return mAvailable;
+ }
+
+ @Override
+ protected void registerInternal() {
+ // no-op
+ }
+
+ @Override
+ protected void unregisterInternal() {
+ // no-op
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/hardware/FakeSensorManager.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeSensorManager.java
index 29b8ab600caf..1deb495b5250 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/hardware/FakeSensorManager.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeSensorManager.java
@@ -11,10 +11,10 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License
+ * limitations under the License.
*/
-package com.android.systemui.utils.hardware;
+package com.android.systemui.util.sensors;
import android.content.Context;
import android.hardware.HardwareBuffer;
@@ -54,7 +54,7 @@ public class FakeSensorManager extends SensorManager {
public static final String TAP_SENSOR_TYPE = "tapSensorType";
- private final MockProximitySensor mMockProximitySensor;
+ private final FakeProximitySensor mFakeProximitySensor;
private final FakeGenericSensor mFakeLightSensor;
private final FakeGenericSensor mFakeTapSensor;
private final FakeGenericSensor[] mSensors;
@@ -68,14 +68,14 @@ public class FakeSensorManager extends SensorManager {
}
mSensors = new FakeGenericSensor[]{
- mMockProximitySensor = new MockProximitySensor(proxSensor),
+ mFakeProximitySensor = new FakeProximitySensor(proxSensor),
mFakeLightSensor = new FakeGenericSensor(createSensor(Sensor.TYPE_LIGHT, null)),
mFakeTapSensor = new FakeGenericSensor(createSensor(99, TAP_SENSOR_TYPE))
};
}
- public MockProximitySensor getMockProximitySensor() {
- return mMockProximitySensor;
+ public FakeProximitySensor getFakeProximitySensor() {
+ return mFakeProximitySensor;
}
public FakeGenericSensor getFakeLightSensor() {
@@ -231,9 +231,9 @@ public class FakeSensorManager extends SensorManager {
setter.invoke(sensor, type);
}
- public class MockProximitySensor extends FakeGenericSensor {
+ public class FakeProximitySensor extends FakeGenericSensor {
- private MockProximitySensor(Sensor sensor) {
+ private FakeProximitySensor(Sensor sensor) {
super(sensor);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorTest.java
new file mode 100644
index 000000000000..fa943e344977
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorTest.java
@@ -0,0 +1,218 @@
+/*
+ * 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.sensors;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Handler;
+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;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class ProximitySensorTest extends SysuiTestCase {
+
+ private ProximitySensor mProximitySensor;
+ private FakeSensorManager.FakeProximitySensor mFakeProximitySensor;
+
+ @Before
+ public void setUp() throws Exception {
+ FakeSensorManager sensorManager = new FakeSensorManager(getContext());
+ AsyncSensorManager asyncSensorManager = new AsyncSensorManager(
+ sensorManager, null, new Handler());
+ mFakeProximitySensor = sensorManager.getFakeProximitySensor();
+ mProximitySensor = new ProximitySensor(getContext(), asyncSensorManager);
+ }
+
+ @Test
+ public void testSingleListener() {
+ TestableListener listener = new TestableListener();
+
+ assertFalse(mProximitySensor.isRegistered());
+ mProximitySensor.register(listener);
+ waitForSensorManager();
+ assertTrue(mProximitySensor.isRegistered());
+ assertNull(listener.mLastEvent);
+
+ mFakeProximitySensor.sendProximityResult(true);
+ assertFalse(listener.mLastEvent.getNear());
+ assertEquals(listener.mCallCount, 1);
+ mFakeProximitySensor.sendProximityResult(false);
+ assertTrue(listener.mLastEvent.getNear());
+ assertEquals(listener.mCallCount, 2);
+
+ mProximitySensor.unregister(listener);
+ waitForSensorManager();
+ }
+
+ @Test
+ public void testMultiListener() {
+ TestableListener listenerA = new TestableListener();
+ TestableListener listenerB = new TestableListener();
+
+ assertFalse(mProximitySensor.isRegistered());
+
+ mProximitySensor.register(listenerA);
+ waitForSensorManager();
+ assertTrue(mProximitySensor.isRegistered());
+ mProximitySensor.register(listenerB);
+ waitForSensorManager();
+ assertTrue(mProximitySensor.isRegistered());
+ assertNull(listenerA.mLastEvent);
+ assertNull(listenerB.mLastEvent);
+
+ mFakeProximitySensor.sendProximityResult(true);
+ assertFalse(listenerA.mLastEvent.getNear());
+ assertFalse(listenerB.mLastEvent.getNear());
+ assertEquals(listenerA.mCallCount, 1);
+ assertEquals(listenerB.mCallCount, 1);
+ mFakeProximitySensor.sendProximityResult(false);
+ assertTrue(listenerA.mLastEvent.getNear());
+ assertTrue(listenerB.mLastEvent.getNear());
+ assertEquals(listenerA.mCallCount, 2);
+ assertEquals(listenerB.mCallCount, 2);
+
+ mProximitySensor.unregister(listenerA);
+ mProximitySensor.unregister(listenerB);
+ waitForSensorManager();
+ }
+
+ @Test
+ public void testUnregister() {
+ TestableListener listener = new TestableListener();
+
+ assertFalse(mProximitySensor.isRegistered());
+ mProximitySensor.register(listener);
+ waitForSensorManager();
+ assertTrue(mProximitySensor.isRegistered());
+ assertNull(listener.mLastEvent);
+
+ mFakeProximitySensor.sendProximityResult(true);
+ assertFalse(listener.mLastEvent.getNear());
+ assertEquals(listener.mCallCount, 1);
+
+ mProximitySensor.unregister(listener);
+ waitForSensorManager();
+ assertFalse(mProximitySensor.isRegistered());
+ }
+
+ @Test
+ public void testPauseAndResume() {
+ TestableListener listener = new TestableListener();
+
+ assertFalse(mProximitySensor.isRegistered());
+ mProximitySensor.register(listener);
+ waitForSensorManager();
+ assertTrue(mProximitySensor.isRegistered());
+ assertNull(listener.mLastEvent);
+
+ mFakeProximitySensor.sendProximityResult(true);
+ assertFalse(listener.mLastEvent.getNear());
+ assertEquals(listener.mCallCount, 1);
+
+ mProximitySensor.pause();
+ waitForSensorManager();
+ assertFalse(mProximitySensor.isRegistered());
+
+ // More events do nothing when paused.
+ mFakeProximitySensor.sendProximityResult(true);
+ assertFalse(listener.mLastEvent.getNear());
+ assertEquals(listener.mCallCount, 1);
+ mFakeProximitySensor.sendProximityResult(false);
+ assertFalse(listener.mLastEvent.getNear());
+ assertEquals(listener.mCallCount, 1);
+
+ mProximitySensor.resume();
+ waitForSensorManager();
+ assertTrue(mProximitySensor.isRegistered());
+ // Still matches our previous call
+ assertFalse(listener.mLastEvent.getNear());
+ assertEquals(listener.mCallCount, 1);
+
+ mFakeProximitySensor.sendProximityResult(true);
+ assertFalse(listener.mLastEvent.getNear());
+ assertEquals(listener.mCallCount, 2);
+
+ mProximitySensor.unregister(listener);
+ waitForSensorManager();
+ assertFalse(mProximitySensor.isRegistered());
+ }
+
+ @Test
+ public void testAlertListeners() {
+ TestableListener listenerA = new TestableListener();
+ TestableListener listenerB = new TestableListener();
+
+ assertFalse(mProximitySensor.isRegistered());
+
+ mProximitySensor.register(listenerA);
+ mProximitySensor.register(listenerB);
+ waitForSensorManager();
+ assertTrue(mProximitySensor.isRegistered());
+ assertNull(listenerA.mLastEvent);
+ assertNull(listenerB.mLastEvent);
+
+ mProximitySensor.alertListeners();
+ assertNull(listenerA.mLastEvent);
+ assertEquals(listenerA.mCallCount, 1);
+ assertNull(listenerB.mLastEvent);
+ assertEquals(listenerB.mCallCount, 1);
+
+ mFakeProximitySensor.sendProximityResult(false);
+ assertTrue(listenerA.mLastEvent.getNear());
+ assertEquals(listenerA.mCallCount, 2);
+ assertTrue(listenerB.mLastEvent.getNear());
+ assertEquals(listenerB.mCallCount, 2);
+
+ mProximitySensor.unregister(listenerA);
+ mProximitySensor.unregister(listenerB);
+ waitForSensorManager();
+ }
+
+ class TestableListener implements ProximitySensor.ProximitySensorListener {
+ ProximitySensor.ProximityEvent mLastEvent;
+ int mCallCount = 0;
+
+ @Override
+ public void onSensorEvent(ProximitySensor.ProximityEvent proximityEvent) {
+ mLastEvent = proximityEvent;
+ mCallCount++;
+ }
+
+ void reset() {
+ mLastEvent = null;
+ mCallCount = 0;
+ }
+ };
+
+ private void waitForSensorManager() {
+ TestableLooper.get(this).processAllMessages();
+ }
+
+}
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..5b826b1c551b 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -7414,6 +7414,17 @@ 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;
+
+ // OPEN: Settings > System > Aware > Aware Display
+ // OS: Q
+ SETTINGS_AWARE_DISPLAY = 1750;
// ---- End Q Constants, all Q constants go above this line ----
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto
index 8ad24894a1b9..fbf6ca52f11c 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -1792,6 +1792,25 @@ message ExperimentValues {
// Indicates if we are logging LinkSpeedCount in metrics
optional bool link_speed_counts_logging_enabled = 4;
+
+ // Duration for evaluating Wifi condition to trigger a data stall
+ // measured in milliseconds
+ optional int32 data_stall_duration_ms = 5;
+
+ // Threshold of Tx throughput below which to trigger a data stall
+ // measured in Kbps
+ optional int32 data_stall_tx_tput_thr_kbps = 6;
+
+ // Threshold of Rx throughput below which to trigger a data stall
+ // measured in Kbps
+ optional int32 data_stall_rx_tput_thr_kbps = 7;
+
+ // Threshold of Tx packet error rate above which to trigger a data stall
+ // in percentage
+ optional int32 data_stall_tx_per_thr = 8;
+
+ // Threshold of CCA level above which to trigger a data stall in percentage
+ optional int32 data_stall_cca_level_thr = 9;
}
message WifiIsUnusableEvent {
diff --git a/services/Android.bp b/services/Android.bp
index 6953e862f68b..60dd8959fc39 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -47,6 +47,11 @@ java_library {
"compat-changeid-annotation-processor",
],
+ required: [
+ // Required by services.backup
+ "BackupEncryption",
+ ],
+
// Uncomment to enable output of certain warnings (deprecated, unchecked)
//javacflags: ["-Xlint"],
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index cdb062d1963c..4f021ad3cee0 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -23,7 +23,7 @@ import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_ACCE
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK;
-import android.accessibilityservice.AccessibilityGestureInfo;
+import android.accessibilityservice.AccessibilityGestureEvent;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.accessibilityservice.IAccessibilityServiceConnection;
@@ -1173,9 +1173,9 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
}
}
- public void notifyGesture(AccessibilityGestureInfo gestureInfo) {
+ public void notifyGesture(AccessibilityGestureEvent gestureEvent) {
mInvocationHandler.obtainMessage(InvocationHandler.MSG_ON_GESTURE,
- gestureInfo).sendToTarget();
+ gestureEvent).sendToTarget();
}
public void notifyClearAccessibilityNodeInfoCache() {
@@ -1264,7 +1264,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
}
}
- private void notifyGestureInternal(AccessibilityGestureInfo gestureInfo) {
+ private void notifyGestureInternal(AccessibilityGestureEvent gestureInfo) {
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
@@ -1469,7 +1469,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
final int type = message.what;
switch (type) {
case MSG_ON_GESTURE: {
- notifyGestureInternal((AccessibilityGestureInfo) message.obj);
+ notifyGestureInternal((AccessibilityGestureEvent) message.obj);
} break;
case MSG_CLEAR_ACCESSIBILITY_CACHE: {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index feb7329bdda6..1f11059392a1 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -27,7 +27,7 @@ import static com.android.internal.util.FunctionalUtils.ignoreRemoteException;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import android.Manifest;
-import android.accessibilityservice.AccessibilityGestureInfo;
+import android.accessibilityservice.AccessibilityGestureEvent;
import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.IAccessibilityServiceClient;
@@ -828,12 +828,17 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
-
- public boolean onGesture(AccessibilityGestureInfo gestureInfo) {
+ /**
+ * Called when a gesture is detected on a display.
+ *
+ * @param gestureEvent the detail of the gesture.
+ * @return true if the event is handled.
+ */
+ public boolean onGesture(AccessibilityGestureEvent gestureEvent) {
synchronized (mLock) {
- boolean handled = notifyGestureLocked(gestureInfo, false);
+ boolean handled = notifyGestureLocked(gestureEvent, false);
if (!handled) {
- handled = notifyGestureLocked(gestureInfo, true);
+ handled = notifyGestureLocked(gestureEvent, true);
}
return handled;
}
@@ -1028,7 +1033,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
- private boolean notifyGestureLocked(AccessibilityGestureInfo gestureInfo, boolean isDefault) {
+ private boolean notifyGestureLocked(AccessibilityGestureEvent gestureEvent, boolean isDefault) {
// TODO: Now we are giving the gestures to the last enabled
// service that can handle them which is the last one
// in our list since we write the last enabled as the
@@ -1042,7 +1047,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
AccessibilityServiceConnection service = state.mBoundServices.get(i);
if (service.mRequestTouchExplorationMode && service.mIsDefault == isDefault) {
- service.notifyGesture(gestureInfo);
+ service.notifyGesture(gestureEvent);
return true;
}
}
@@ -2200,6 +2205,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
synchronized(mLock) {
final UserState userState = getUserStateLocked(mCurrentUserId);
+ if (userState.mServiceToEnableWithShortcut == null) {
+ return null;
+ }
return userState.mServiceToEnableWithShortcut.flattenToString();
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/AccessibilityGestureDetector.java b/services/accessibility/java/com/android/server/accessibility/gestures/AccessibilityGestureDetector.java
index 9101a01bc8fe..7e8fb295d036 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/AccessibilityGestureDetector.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/AccessibilityGestureDetector.java
@@ -16,7 +16,7 @@
package com.android.server.accessibility.gestures;
-import android.accessibilityservice.AccessibilityGestureInfo;
+import android.accessibilityservice.AccessibilityGestureEvent;
import android.accessibilityservice.AccessibilityService;
import android.content.Context;
import android.gesture.GesturePoint;
@@ -119,11 +119,11 @@ class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListen
/**
* Called when an event stream is recognized as a gesture.
*
- * @param gestureInfo Information about the gesture.
+ * @param gestureEvent Information about the gesture.
*
* @return true if the event is consumed, else false
*/
- boolean onGestureCompleted(AccessibilityGestureInfo gestureInfo);
+ boolean onGestureCompleted(AccessibilityGestureEvent gestureEvent);
/**
* Called when the system has decided an event stream doesn't match any
@@ -567,19 +567,19 @@ class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListen
switch (direction) {
case LEFT:
return mListener.onGestureCompleted(
- new AccessibilityGestureInfo(AccessibilityService.GESTURE_SWIPE_LEFT,
+ new AccessibilityGestureEvent(AccessibilityService.GESTURE_SWIPE_LEFT,
displayId));
case RIGHT:
return mListener.onGestureCompleted(
- new AccessibilityGestureInfo(AccessibilityService.GESTURE_SWIPE_RIGHT,
+ new AccessibilityGestureEvent(AccessibilityService.GESTURE_SWIPE_RIGHT,
displayId));
case UP:
return mListener.onGestureCompleted(
- new AccessibilityGestureInfo(AccessibilityService.GESTURE_SWIPE_UP,
+ new AccessibilityGestureEvent(AccessibilityService.GESTURE_SWIPE_UP,
displayId));
case DOWN:
return mListener.onGestureCompleted(
- new AccessibilityGestureInfo(AccessibilityService.GESTURE_SWIPE_DOWN,
+ new AccessibilityGestureEvent(AccessibilityService.GESTURE_SWIPE_DOWN,
displayId));
default:
// Do nothing.
@@ -600,7 +600,7 @@ class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListen
int segmentDirection1 = toDirection(dX1, dY1);
int gestureId = DIRECTIONS_TO_GESTURE_ID[segmentDirection0][segmentDirection1];
return mListener.onGestureCompleted(
- new AccessibilityGestureInfo(gestureId, displayId));
+ new AccessibilityGestureEvent(gestureId, displayId));
}
// else if (path.size() < 2 || 3 < path.size()) then no gesture recognized.
return mListener.onGestureCancelled(event, policyFlags);
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java b/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java
new file mode 100644
index 000000000000..dc7a9aaf966d
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java
@@ -0,0 +1,309 @@
+/*
+ * 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.accessibility.gestures;
+
+import static com.android.server.accessibility.gestures.TouchExplorer.DEBUG;
+import static com.android.server.accessibility.gestures.TouchState.ALL_POINTER_ID_BITS;
+import static com.android.server.accessibility.gestures.TouchState.MAX_POINTER_COUNT;
+
+import android.content.Context;
+import android.util.Slog;
+import android.view.MotionEvent;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+
+import com.android.server.accessibility.AccessibilityManagerService;
+import com.android.server.accessibility.EventStreamTransformation;
+import com.android.server.policy.WindowManagerPolicy;
+
+/**
+ * This class dispatches motion events and accessibility events relating to touch exploration and
+ * gesture dispatch. TouchExplorer is responsible for insuring that the receiver of motion events is
+ * set correctly so that events go to the right place.
+ */
+class EventDispatcher {
+ private static final String LOG_TAG = "EventDispatcher";
+
+ private final AccessibilityManagerService mAms;
+ private Context mContext;
+ // The receiver of motion events.
+ private EventStreamTransformation mReceiver;
+ // Keep track of which pointers sent to the system are down.
+ private int mInjectedPointersDown;
+
+ // The time of the last injected down.
+ private long mLastInjectedDownEventTime;
+
+ // The last injected hover event.
+ private MotionEvent mLastInjectedHoverEvent;
+ private TouchState mState;
+
+ EventDispatcher(
+ Context context,
+ AccessibilityManagerService ams,
+ EventStreamTransformation receiver,
+ TouchState state) {
+ mContext = context;
+ mAms = ams;
+ mReceiver = receiver;
+ mState = state;
+ }
+
+ public void setReceiver(EventStreamTransformation receiver) {
+ mReceiver = receiver;
+ }
+
+ /**
+ * Sends an event.
+ *
+ * @param prototype The prototype from which to create the injected events.
+ * @param action The action of the event.
+ * @param pointerIdBits The bits of the pointers to send.
+ * @param policyFlags The policy flags associated with the event.
+ */
+ void sendMotionEvent(MotionEvent prototype, int action, int pointerIdBits, int policyFlags) {
+ prototype.setAction(action);
+
+ MotionEvent event = null;
+ if (pointerIdBits == ALL_POINTER_ID_BITS) {
+ event = prototype;
+ } else {
+ try {
+ event = prototype.split(pointerIdBits);
+ } catch (IllegalArgumentException e) {
+ Slog.e(LOG_TAG, "sendMotionEvent: Failed to split motion event: " + e);
+ return;
+ }
+ }
+ if (action == MotionEvent.ACTION_DOWN) {
+ event.setDownTime(event.getEventTime());
+ } else {
+ event.setDownTime(getLastInjectedDownEventTime());
+ }
+ if (DEBUG) {
+ Slog.d(
+ LOG_TAG,
+ "Injecting event: "
+ + event
+ + ", policyFlags=0x"
+ + Integer.toHexString(policyFlags));
+ }
+
+ // Make sure that the user will see the event.
+ policyFlags |= WindowManagerPolicy.FLAG_PASS_TO_USER;
+ // TODO: For now pass null for the raw event since the touch
+ // explorer is the last event transformation and it does
+ // not care about the raw event.
+ if (mReceiver != null) {
+ mReceiver.onMotionEvent(event, null, policyFlags);
+ } else {
+ Slog.e(LOG_TAG, "Error sending event: no receiver specified.");
+ }
+ updateState(event);
+
+ if (event != prototype) {
+ event.recycle();
+ }
+ }
+
+ /**
+ * Sends an accessibility event of the given type.
+ *
+ * @param type The event type.
+ */
+ void sendAccessibilityEvent(int type) {
+ AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(mContext);
+ if (accessibilityManager.isEnabled()) {
+ AccessibilityEvent event = AccessibilityEvent.obtain(type);
+ event.setWindowId(mAms.getActiveWindowId());
+ accessibilityManager.sendAccessibilityEvent(event);
+ if (DEBUG) {
+ Slog.d(
+ LOG_TAG,
+ "Sending accessibility event" + AccessibilityEvent.eventTypeToString(type));
+ }
+ }
+ // Todo: get rid of this and have TouchState control the sending of events rather than react
+ // to it.
+ mState.onInjectedAccessibilityEvent(type);
+ }
+
+ /**
+ * Processes an injected {@link MotionEvent} event.
+ *
+ * @param event The event to process.
+ */
+ void updateState(MotionEvent event) {
+ final int action = event.getActionMasked();
+ final int pointerId = event.getPointerId(event.getActionIndex());
+ final int pointerFlag = (1 << pointerId);
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ case MotionEvent.ACTION_POINTER_DOWN:
+ mInjectedPointersDown |= pointerFlag;
+ mLastInjectedDownEventTime = event.getDownTime();
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_POINTER_UP:
+ mInjectedPointersDown &= ~pointerFlag;
+ if (mInjectedPointersDown == 0) {
+ mLastInjectedDownEventTime = 0;
+ }
+ break;
+ case MotionEvent.ACTION_HOVER_ENTER:
+ case MotionEvent.ACTION_HOVER_MOVE:
+ case MotionEvent.ACTION_HOVER_EXIT:
+ if (mLastInjectedHoverEvent != null) {
+ mLastInjectedHoverEvent.recycle();
+ }
+ mLastInjectedHoverEvent = MotionEvent.obtain(event);
+ break;
+ }
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "Injected pointer:\n" + toString());
+ }
+ }
+
+ /** Clears the internals state. */
+ public void clear() {
+ mInjectedPointersDown = 0;
+ }
+
+ /** @return The time of the last injected down event. */
+ public long getLastInjectedDownEventTime() {
+ return mLastInjectedDownEventTime;
+ }
+
+ /** @return The number of down pointers injected to the view hierarchy. */
+ public int getInjectedPointerDownCount() {
+ return Integer.bitCount(mInjectedPointersDown);
+ }
+
+ /** @return The bits of the injected pointers that are down. */
+ public int getInjectedPointersDown() {
+ return mInjectedPointersDown;
+ }
+
+ /**
+ * Whether an injected pointer is down.
+ *
+ * @param pointerId The unique pointer id.
+ * @return True if the pointer is down.
+ */
+ public boolean isInjectedPointerDown(int pointerId) {
+ final int pointerFlag = (1 << pointerId);
+ return (mInjectedPointersDown & pointerFlag) != 0;
+ }
+
+ /** @return The the last injected hover event. */
+ public MotionEvent getLastInjectedHoverEvent() {
+ return mLastInjectedHoverEvent;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("=========================");
+ builder.append("\nDown pointers #");
+ builder.append(Integer.bitCount(mInjectedPointersDown));
+ builder.append(" [ ");
+ for (int i = 0; i < MAX_POINTER_COUNT; i++) {
+ if ((mInjectedPointersDown & i) != 0) {
+ builder.append(i);
+ builder.append(" ");
+ }
+ }
+ builder.append("]");
+ builder.append("\n=========================");
+ return builder.toString();
+ }
+
+ /**
+ * Computes the action for an injected event based on a masked action and a pointer index.
+ *
+ * @param actionMasked The masked action.
+ * @param pointerIndex The index of the pointer which has changed.
+ * @return The action to be used for injection.
+ */
+ private int computeInjectionAction(int actionMasked, int pointerIndex) {
+ switch (actionMasked) {
+ case MotionEvent.ACTION_DOWN:
+ case MotionEvent.ACTION_POINTER_DOWN:
+ // Compute the action based on how many down pointers are injected.
+ if (getInjectedPointerDownCount() == 0) {
+ return MotionEvent.ACTION_DOWN;
+ } else {
+ return (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT)
+ | MotionEvent.ACTION_POINTER_DOWN;
+ }
+ case MotionEvent.ACTION_POINTER_UP:
+ // Compute the action based on how many down pointers are injected.
+ if (getInjectedPointerDownCount() == 1) {
+ return MotionEvent.ACTION_UP;
+ } else {
+ return (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT)
+ | MotionEvent.ACTION_POINTER_UP;
+ }
+ default:
+ return actionMasked;
+ }
+ }
+ /**
+ * Sends down events to the view hierarchy for all pointers which are not already being
+ * delivered i.e. pointers that are not yet injected.
+ *
+ * @param prototype The prototype from which to create the injected events.
+ * @param policyFlags The policy flags associated with the event.
+ */
+ void sendDownForAllNotInjectedPointers(MotionEvent prototype, int policyFlags) {
+
+ // Inject the injected pointers.
+ int pointerIdBits = 0;
+ final int pointerCount = prototype.getPointerCount();
+ for (int i = 0; i < pointerCount; i++) {
+ final int pointerId = prototype.getPointerId(i);
+ // Do not send event for already delivered pointers.
+ if (!isInjectedPointerDown(pointerId)) {
+ pointerIdBits |= (1 << pointerId);
+ final int action = computeInjectionAction(MotionEvent.ACTION_DOWN, i);
+ sendMotionEvent(prototype, action, pointerIdBits, policyFlags);
+ }
+ }
+ }
+
+ /**
+ * Sends up events to the view hierarchy for all pointers which are already being delivered i.e.
+ * pointers that are injected.
+ *
+ * @param prototype The prototype from which to create the injected events.
+ * @param policyFlags The policy flags associated with the event.
+ */
+ void sendUpForInjectedDownPointers(MotionEvent prototype, int policyFlags) {
+ int pointerIdBits = 0;
+ final int pointerCount = prototype.getPointerCount();
+ for (int i = 0; i < pointerCount; i++) {
+ final int pointerId = prototype.getPointerId(i);
+ // Skip non injected down pointers.
+ if (!isInjectedPointerDown(pointerId)) {
+ continue;
+ }
+ pointerIdBits |= (1 << pointerId);
+ final int action = computeInjectionAction(MotionEvent.ACTION_UP, i);
+ sendMotionEvent(prototype, action, pointerIdBits, policyFlags);
+ }
+ }
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index a338b901d24c..f4ac82157d04 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -20,7 +20,7 @@ import static android.view.MotionEvent.INVALID_POINTER_ID;
import static com.android.server.accessibility.gestures.TouchState.ALL_POINTER_ID_BITS;
-import android.accessibilityservice.AccessibilityGestureInfo;
+import android.accessibilityservice.AccessibilityGestureEvent;
import android.content.Context;
import android.graphics.Point;
import android.os.Handler;
@@ -29,11 +29,11 @@ import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import com.android.server.accessibility.AccessibilityManagerService;
import com.android.server.accessibility.BaseEventStreamTransformation;
+import com.android.server.accessibility.EventStreamTransformation;
import com.android.server.policy.WindowManagerPolicy;
import java.util.ArrayList;
@@ -61,7 +61,7 @@ import java.util.List;
public class TouchExplorer extends BaseEventStreamTransformation
implements AccessibilityGestureDetector.Listener {
- private static final boolean DEBUG = false;
+ static final boolean DEBUG = false;
// Tag for logging received events.
private static final String LOG_TAG = "TouchExplorer";
@@ -109,8 +109,7 @@ public class TouchExplorer extends BaseEventStreamTransformation
// Helper class to track received pointers.
private final TouchState.ReceivedPointerTracker mReceivedPointerTracker;
- // Helper class to track injected pointers.
- private final TouchState.InjectedPointerTracker mInjectedPointerTracker;
+ private final EventDispatcher mDispatcher;
// Handle to the accessibility manager service.
private final AccessibilityManagerService mAms;
@@ -148,7 +147,7 @@ public class TouchExplorer extends BaseEventStreamTransformation
mAms = service;
mState = new TouchState();
mReceivedPointerTracker = mState.getReceivedPointerTracker();
- mInjectedPointerTracker = mState.getInjectedPointerTracker();
+ mDispatcher = new EventDispatcher(context, mAms, super.getNext(), mState);
mDetermineUserIntentTimeout = ViewConfiguration.getDoubleTapTimeout();
mDoubleTapSlop = ViewConfiguration.get(context).getScaledDoubleTapSlop();
mHandler = new Handler(context.getMainLooper());
@@ -197,10 +196,10 @@ public class TouchExplorer extends BaseEventStreamTransformation
} else if (mState.isDragging()) {
mDraggingPointerId = INVALID_POINTER_ID;
// Send exit to all pointers that we have delivered.
- sendUpForInjectedDownPointers(event, policyFlags);
+ mDispatcher.sendUpForInjectedDownPointers(event, policyFlags);
} else if (mState.isDelegating()) {
// Send exit to all pointers that we have delivered.
- sendUpForInjectedDownPointers(event, policyFlags);
+ mDispatcher.sendUpForInjectedDownPointers(event, policyFlags);
} else if (mState.isGestureDetecting()) {
// No state specific cleanup required.
}
@@ -271,7 +270,8 @@ public class TouchExplorer extends BaseEventStreamTransformation
if (mSendTouchExplorationEndDelayed.isPending()
&& eventType == AccessibilityEvent.TYPE_VIEW_HOVER_EXIT) {
mSendTouchExplorationEndDelayed.cancel();
- sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END);
+ mDispatcher.sendAccessibilityEvent(
+ AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END);
}
// The event for touch interaction end should be strictly after the
@@ -279,7 +279,7 @@ public class TouchExplorer extends BaseEventStreamTransformation
if (mSendTouchInteractionEndDelayed.isPending()
&& eventType == AccessibilityEvent.TYPE_VIEW_HOVER_EXIT) {
mSendTouchInteractionEndDelayed.cancel();
- sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
+ mDispatcher.sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
}
super.onAccessibilityEvent(event);
}
@@ -318,7 +318,7 @@ public class TouchExplorer extends BaseEventStreamTransformation
}
// Announce the end of a new touch interaction.
- sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
+ mDispatcher.sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
// Try to use the standard accessibility API to click
if (!mAms.performActionOnAccessibilityFocusedItem(
@@ -338,19 +338,19 @@ public class TouchExplorer extends BaseEventStreamTransformation
mExitGestureDetectionModeDelayed.post();
// Send accessibility event to announce the start
// of gesture recognition.
- sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_START);
+ mDispatcher.sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_START);
return false;
}
@Override
- public boolean onGestureCompleted(AccessibilityGestureInfo gestureInfo) {
+ public boolean onGestureCompleted(AccessibilityGestureEvent gestureEvent) {
if (!mState.isGestureDetecting()) {
return false;
}
endGestureDetection(true);
- mAms.onGesture(gestureInfo);
+ mAms.onGesture(gestureEvent);
return true;
}
@@ -371,7 +371,8 @@ public class TouchExplorer extends BaseEventStreamTransformation
mSendHoverEnterAndMoveDelayed.addEvent(event);
mSendHoverEnterAndMoveDelayed.forceSendAndRemove();
mSendHoverExitDelayed.cancel();
- sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits, policyFlags);
+ mDispatcher.sendMotionEvent(
+ event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits, policyFlags);
return true;
}
}
@@ -417,7 +418,7 @@ public class TouchExplorer extends BaseEventStreamTransformation
if (!mGestureDetector.firstTapDetected() && mState.isClear()) {
mSendTouchExplorationEndDelayed.forceSendAndRemove();
mSendTouchInteractionEndDelayed.forceSendAndRemove();
- sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_START);
+ mDispatcher.sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_START);
} else {
// Let gesture to handle to avoid duplicated TYPE_TOUCH_INTERACTION_END event.
mSendTouchInteractionEndDelayed.cancel();
@@ -536,18 +537,19 @@ public class TouchExplorer extends BaseEventStreamTransformation
mState.startDragging();
mDraggingPointerId = pointerId;
event.setEdgeFlags(mReceivedPointerTracker.getLastReceivedDownEdgeFlags());
- sendMotionEvent(event, MotionEvent.ACTION_DOWN, pointerIdBits, policyFlags);
+ mDispatcher.sendMotionEvent(
+ event, MotionEvent.ACTION_DOWN, pointerIdBits, policyFlags);
} else {
// Two pointers moving arbitrary are delegated to the view hierarchy.
mState.startDelegating();
- sendDownForAllNotInjectedPointers(event, policyFlags);
+ mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags);
}
break;
default:
// More than two pointers are delegated to the view hierarchy.
mState.startDelegating();
event = MotionEvent.obtainNoHistory(event);
- sendDownForAllNotInjectedPointers(event, policyFlags);
+ mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags);
break;
}
}
@@ -585,7 +587,8 @@ public class TouchExplorer extends BaseEventStreamTransformation
case 1:
// Touch exploration.
sendTouchExplorationGestureStartAndHoverEnterIfNeeded(policyFlags);
- sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits, policyFlags);
+ mDispatcher.sendMotionEvent(
+ event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits, policyFlags);
break;
case 2:
if (mSendHoverEnterAndMoveDelayed.isPending()) {
@@ -658,9 +661,10 @@ public class TouchExplorer extends BaseEventStreamTransformation
// goes down => delegate the three pointers to the view hierarchy
mState.startDelegating();
if (mDraggingPointerId != INVALID_POINTER_ID) {
- sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags);
+ mDispatcher.sendMotionEvent(
+ event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags);
}
- sendDownForAllNotInjectedPointers(event, policyFlags);
+ mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags);
} break;
case MotionEvent.ACTION_MOVE: {
if (mDraggingPointerId == INVALID_POINTER_ID) {
@@ -672,21 +676,12 @@ public class TouchExplorer extends BaseEventStreamTransformation
} break;
case 2: {
if (isDraggingGesture(event)) {
- // Adjust event location to the middle location of the two pointers.
- final float firstPtrX = event.getX(0);
- final float firstPtrY = event.getY(0);
- final float secondPtrX = event.getX(1);
- final float secondPtrY = event.getY(1);
- final int pointerIndex = event.findPointerIndex(mDraggingPointerId);
- final float deltaX =
- (pointerIndex == 0) ? (secondPtrX - firstPtrX)
- : (firstPtrX - secondPtrX);
- final float deltaY =
- (pointerIndex == 0) ? (secondPtrY - firstPtrY)
- : (firstPtrY - secondPtrY);
- event.offsetLocation(deltaX / 2, deltaY / 2);
// If still dragging send a drag event.
- sendMotionEvent(event, MotionEvent.ACTION_MOVE, pointerIdBits,
+ adjustEventLocationForDrag(event);
+ mDispatcher.sendMotionEvent(
+ event,
+ MotionEvent.ACTION_MOVE,
+ pointerIdBits,
policyFlags);
} else {
// The two pointers are moving either in different directions or
@@ -695,20 +690,20 @@ public class TouchExplorer extends BaseEventStreamTransformation
// Remove move history before send injected non-move events
event = MotionEvent.obtainNoHistory(event);
// Send an event to the end of the drag gesture.
- sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits,
+ mDispatcher.sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits,
policyFlags);
// Deliver all pointers to the view hierarchy.
- sendDownForAllNotInjectedPointers(event, policyFlags);
+ mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags);
}
} break;
default: {
mState.startDelegating();
event = MotionEvent.obtainNoHistory(event);
// Send an event to the end of the drag gesture.
- sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits,
+ mDispatcher.sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits,
policyFlags);
// Deliver all pointers to the view hierarchy.
- sendDownForAllNotInjectedPointers(event, policyFlags);
+ mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags);
}
}
} break;
@@ -716,20 +711,22 @@ public class TouchExplorer extends BaseEventStreamTransformation
final int pointerId = event.getPointerId(event.getActionIndex());
if (pointerId == mDraggingPointerId) {
mDraggingPointerId = INVALID_POINTER_ID;
- // Send an event to the end of the drag gesture.
- sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags);
+ // Send an event to the end of the drag gesture.
+ mDispatcher.sendMotionEvent(
+ event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags);
}
} break;
case MotionEvent.ACTION_UP: {
mAms.onTouchInteractionEnd();
// Announce the end of a new touch interaction.
- sendAccessibilityEvent(
+ mDispatcher.sendAccessibilityEvent(
AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
final int pointerId = event.getPointerId(event.getActionIndex());
if (pointerId == mDraggingPointerId) {
mDraggingPointerId = INVALID_POINTER_ID;
// Send an event to the end of the drag gesture.
- sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags);
+ mDispatcher.sendMotionEvent(
+ event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags);
}
} break;
}
@@ -751,16 +748,18 @@ public class TouchExplorer extends BaseEventStreamTransformation
}
case MotionEvent.ACTION_UP: {
// Deliver the event.
- sendMotionEvent(event, event.getAction(), ALL_POINTER_ID_BITS, policyFlags);
+ mDispatcher.sendMotionEvent(
+ event, event.getAction(), ALL_POINTER_ID_BITS, policyFlags);
// Announce the end of a the touch interaction.
mAms.onTouchInteractionEnd();
- sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
+ mDispatcher.sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
} break;
default: {
- // Deliver the event.
- sendMotionEvent(event, event.getAction(), ALL_POINTER_ID_BITS, policyFlags);
+ // Deliver the event.
+ mDispatcher.sendMotionEvent(
+ event, event.getAction(), ALL_POINTER_ID_BITS, policyFlags);
}
}
}
@@ -769,57 +768,15 @@ public class TouchExplorer extends BaseEventStreamTransformation
mAms.onTouchInteractionEnd();
// Announce the end of the gesture recognition.
- sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_END);
+ mDispatcher.sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_END);
// Don't announce the end of a the touch interaction if users didn't lift their fingers.
if (interactionEnd) {
- sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
+ mDispatcher.sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
}
mExitGestureDetectionModeDelayed.cancel();
}
- /**
- * Sends an accessibility event of the given type.
- *
- * @param type The event type.
- */
- private void sendAccessibilityEvent(int type) {
- AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(mContext);
- if (accessibilityManager.isEnabled()) {
- AccessibilityEvent event = AccessibilityEvent.obtain(type);
- event.setWindowId(mAms.getActiveWindowId());
- accessibilityManager.sendAccessibilityEvent(event);
- if (DEBUG) {
- Slog.d(
- LOG_TAG,
- "Sending accessibility event" + AccessibilityEvent.eventTypeToString(type));
- }
- }
- mState.onInjectedAccessibilityEvent(type);
- }
-
- /**
- * Sends down events to the view hierarchy for all pointers which are
- * not already being delivered i.e. pointers that are not yet injected.
- *
- * @param prototype The prototype from which to create the injected events.
- * @param policyFlags The policy flags associated with the event.
- */
- private void sendDownForAllNotInjectedPointers(MotionEvent prototype, int policyFlags) {
-
- // Inject the injected pointers.
- int pointerIdBits = 0;
- final int pointerCount = prototype.getPointerCount();
- for (int i = 0; i < pointerCount; i++) {
- final int pointerId = prototype.getPointerId(i);
- // Do not send event for already delivered pointers.
- if (!mInjectedPointerTracker.isInjectedPointerDown(pointerId)) {
- pointerIdBits |= (1 << pointerId);
- final int action = computeInjectionAction(MotionEvent.ACTION_DOWN, i);
- sendMotionEvent(prototype, action, pointerIdBits, policyFlags);
- }
- }
- }
/**
* Sends the exit events if needed. Such events are hover exit and touch explore
@@ -828,13 +785,14 @@ public class TouchExplorer extends BaseEventStreamTransformation
* @param policyFlags The policy flags associated with the event.
*/
private void sendHoverExitAndTouchExplorationGestureEndIfNeeded(int policyFlags) {
- MotionEvent event = mInjectedPointerTracker.getLastInjectedHoverEvent();
+ MotionEvent event = mDispatcher.getLastInjectedHoverEvent();
if (event != null && event.getActionMasked() != MotionEvent.ACTION_HOVER_EXIT) {
final int pointerIdBits = event.getPointerIdBits();
if (!mSendTouchExplorationEndDelayed.isPending()) {
mSendTouchExplorationEndDelayed.post();
}
- sendMotionEvent(event, MotionEvent.ACTION_HOVER_EXIT, pointerIdBits, policyFlags);
+ mDispatcher.sendMotionEvent(
+ event, MotionEvent.ACTION_HOVER_EXIT, pointerIdBits, policyFlags);
}
}
@@ -845,115 +803,14 @@ public class TouchExplorer extends BaseEventStreamTransformation
* @param policyFlags The policy flags associated with the event.
*/
private void sendTouchExplorationGestureStartAndHoverEnterIfNeeded(int policyFlags) {
- MotionEvent event = mInjectedPointerTracker.getLastInjectedHoverEvent();
+ MotionEvent event = mDispatcher.getLastInjectedHoverEvent();
if (event != null && event.getActionMasked() == MotionEvent.ACTION_HOVER_EXIT) {
final int pointerIdBits = event.getPointerIdBits();
- sendMotionEvent(event, MotionEvent.ACTION_HOVER_ENTER, pointerIdBits, policyFlags);
+ mDispatcher.sendMotionEvent(
+ event, MotionEvent.ACTION_HOVER_ENTER, pointerIdBits, policyFlags);
}
}
- /**
- * Sends up events to the view hierarchy for all pointers which are
- * already being delivered i.e. pointers that are injected.
- *
- * @param prototype The prototype from which to create the injected events.
- * @param policyFlags The policy flags associated with the event.
- */
- private void sendUpForInjectedDownPointers(MotionEvent prototype, int policyFlags) {
- int pointerIdBits = 0;
- final int pointerCount = prototype.getPointerCount();
- for (int i = 0; i < pointerCount; i++) {
- final int pointerId = prototype.getPointerId(i);
- // Skip non injected down pointers.
- if (!mInjectedPointerTracker.isInjectedPointerDown(pointerId)) {
- continue;
- }
- pointerIdBits |= (1 << pointerId);
- final int action = computeInjectionAction(MotionEvent.ACTION_UP, i);
- sendMotionEvent(prototype, action, pointerIdBits, policyFlags);
- }
- }
-
- /**
- * Sends an event.
- *
- * @param prototype The prototype from which to create the injected events.
- * @param action The action of the event.
- * @param pointerIdBits The bits of the pointers to send.
- * @param policyFlags The policy flags associated with the event.
- */
- private void sendMotionEvent(MotionEvent prototype, int action, int pointerIdBits,
- int policyFlags) {
- prototype.setAction(action);
-
- MotionEvent event = null;
- if (pointerIdBits == ALL_POINTER_ID_BITS) {
- event = prototype;
- } else {
- try {
- event = prototype.split(pointerIdBits);
- } catch (IllegalArgumentException e) {
- Slog.e(LOG_TAG, "sendMotionEvent: Failed to split motion event: " + e);
- return;
- }
- }
- if (action == MotionEvent.ACTION_DOWN) {
- event.setDownTime(event.getEventTime());
- } else {
- event.setDownTime(mInjectedPointerTracker.getLastInjectedDownEventTime());
- }
- if (DEBUG) {
- Slog.d(LOG_TAG, "Injecting event: " + event + ", policyFlags=0x"
- + Integer.toHexString(policyFlags));
- }
-
- // Make sure that the user will see the event.
- policyFlags |= WindowManagerPolicy.FLAG_PASS_TO_USER;
- // TODO: For now pass null for the raw event since the touch
- // explorer is the last event transformation and it does
- // not care about the raw event.
- super.onMotionEvent(event, null, policyFlags);
-
- mInjectedPointerTracker.onMotionEvent(event);
-
- if (event != prototype) {
- event.recycle();
- }
- }
-
- /**
- * Computes the action for an injected event based on a masked action
- * and a pointer index.
- *
- * @param actionMasked The masked action.
- * @param pointerIndex The index of the pointer which has changed.
- * @return The action to be used for injection.
- */
- private int computeInjectionAction(int actionMasked, int pointerIndex) {
- switch (actionMasked) {
- case MotionEvent.ACTION_DOWN:
- case MotionEvent.ACTION_POINTER_DOWN: {
- // Compute the action based on how many down pointers are injected.
- if (mInjectedPointerTracker.getInjectedPointerDownCount() == 0) {
- return MotionEvent.ACTION_DOWN;
- } else {
- return (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT)
- | MotionEvent.ACTION_POINTER_DOWN;
- }
- }
- case MotionEvent.ACTION_POINTER_UP: {
- // Compute the action based on how many down pointers are injected.
- if (mInjectedPointerTracker.getInjectedPointerDownCount() == 1) {
- return MotionEvent.ACTION_UP;
- } else {
- return (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT)
- | MotionEvent.ACTION_POINTER_UP;
- }
- }
- default:
- return actionMasked;
- }
- }
/**
* Determines whether a two pointer gesture is a dragging one.
@@ -978,10 +835,34 @@ public class TouchExplorer extends BaseEventStreamTransformation
MAX_DRAGGING_ANGLE_COS);
}
+ /**
+ * Adjust the location of an injected event when performing a drag The new location will be in
+ * between the two fingers touching the screen.
+ */
+ private void adjustEventLocationForDrag(MotionEvent event) {
+
+ final float firstPtrX = event.getX(0);
+ final float firstPtrY = event.getY(0);
+ final float secondPtrX = event.getX(1);
+ final float secondPtrY = event.getY(1);
+ final int pointerIndex = event.findPointerIndex(mDraggingPointerId);
+ final float deltaX =
+ (pointerIndex == 0) ? (secondPtrX - firstPtrX) : (firstPtrX - secondPtrX);
+ final float deltaY =
+ (pointerIndex == 0) ? (secondPtrY - firstPtrY) : (firstPtrY - secondPtrY);
+ event.offsetLocation(deltaX / 2, deltaY / 2);
+ }
+
public TouchState getState() {
return mState;
}
+ @Override
+ public void setNext(EventStreamTransformation next) {
+ mDispatcher.setReceiver(next);
+ super.setNext(next);
+ }
+
/**
* Class for delayed exiting from gesture detecting mode.
*/
@@ -998,7 +879,7 @@ public class TouchExplorer extends BaseEventStreamTransformation
@Override
public void run() {
// Announce the end of gesture recognition.
- sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_END);
+ mDispatcher.sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_END);
clear();
}
}
@@ -1055,11 +936,12 @@ public class TouchExplorer extends BaseEventStreamTransformation
public void run() {
// Send an accessibility event to announce the touch exploration start.
- sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START);
+ mDispatcher.sendAccessibilityEvent(
+ AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START);
if (!mEvents.isEmpty()) {
// Deliver a down event.
- sendMotionEvent(mEvents.get(0), MotionEvent.ACTION_HOVER_ENTER,
+ mDispatcher.sendMotionEvent(mEvents.get(0), MotionEvent.ACTION_HOVER_ENTER,
mPointerIdBits, mPolicyFlags);
if (DEBUG) {
Slog.d(LOG_TAG_SEND_HOVER_DELAYED,
@@ -1069,7 +951,7 @@ public class TouchExplorer extends BaseEventStreamTransformation
// Deliver move events.
final int eventCount = mEvents.size();
for (int i = 1; i < eventCount; i++) {
- sendMotionEvent(mEvents.get(i), MotionEvent.ACTION_HOVER_MOVE,
+ mDispatcher.sendMotionEvent(mEvents.get(i), MotionEvent.ACTION_HOVER_MOVE,
mPointerIdBits, mPolicyFlags);
if (DEBUG) {
Slog.d(LOG_TAG_SEND_HOVER_DELAYED,
@@ -1129,7 +1011,7 @@ public class TouchExplorer extends BaseEventStreamTransformation
Slog.d(LOG_TAG_SEND_HOVER_DELAYED, "Injecting motion event:"
+ " ACTION_HOVER_EXIT");
}
- sendMotionEvent(mPrototype, MotionEvent.ACTION_HOVER_EXIT,
+ mDispatcher.sendMotionEvent(mPrototype, MotionEvent.ACTION_HOVER_EXIT,
mPointerIdBits, mPolicyFlags);
if (!mSendTouchExplorationEndDelayed.isPending()) {
mSendTouchExplorationEndDelayed.cancel();
@@ -1173,7 +1055,7 @@ public class TouchExplorer extends BaseEventStreamTransformation
@Override
public void run() {
- sendAccessibilityEvent(mEventType);
+ mDispatcher.sendAccessibilityEvent(mEventType);
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
index 17e969a1aa49..49938fa4c6b9 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
@@ -18,6 +18,8 @@ package com.android.server.accessibility.gestures;
import static android.view.MotionEvent.INVALID_POINTER_ID;
+import static com.android.server.accessibility.gestures.TouchExplorer.DEBUG;
+
import android.annotation.IntDef;
import android.util.Slog;
import android.view.MotionEvent;
@@ -29,14 +31,12 @@ import android.view.accessibility.AccessibilityEvent;
* dispatch.
*/
public class TouchState {
-
- private static final boolean DEBUG = false;
private static final String LOG_TAG = "TouchState";
// Pointer-related constants
// This constant captures the current implementation detail that
// pointer IDs are between 0 and 31 inclusive (subject to change).
// (See MAX_POINTER_ID in frameworks/base/include/ui/Input.h)
- private static final int MAX_POINTER_COUNT = 32;
+ static final int MAX_POINTER_COUNT = 32;
// Constant referring to the ids bits of all pointers.
public static final int ALL_POINTER_ID_BITS = 0xFFFFFFFF;
@@ -71,13 +71,9 @@ public class TouchState {
// Helper class to track received pointers.
// Todo: collapse or hide this class so multiple classes don't modify it.
private final ReceivedPointerTracker mReceivedPointerTracker;
- // Helper class to track injected pointers.
- // Todo: collapse or hide this class so multiple classes don't modify it.
- private final InjectedPointerTracker mInjectedPointerTracker;
public TouchState() {
mReceivedPointerTracker = new ReceivedPointerTracker();
- mInjectedPointerTracker = new InjectedPointerTracker();
}
/** Clears the internal shared state. */
@@ -85,16 +81,6 @@ public class TouchState {
setState(STATE_CLEAR);
// Reset the pointer trackers.
mReceivedPointerTracker.clear();
- mInjectedPointerTracker.clear();
- }
-
- /**
- * Updates the state in response to a hover event dispatched by TouchExplorer.
- *
- * @param event The event sent from TouchExplorer.
- */
- public void onInjectedMotionEvent(MotionEvent event) {
- mInjectedPointerTracker.onMotionEvent(event);
}
/**
@@ -226,117 +212,10 @@ public class TouchState {
}
}
- public InjectedPointerTracker getInjectedPointerTracker() {
- return mInjectedPointerTracker;
- }
-
public ReceivedPointerTracker getReceivedPointerTracker() {
return mReceivedPointerTracker;
}
- /** This class tracks the up/down state of each pointer. It does not track movement. */
- class InjectedPointerTracker {
- private static final String LOG_TAG_INJECTED_POINTER_TRACKER = "InjectedPointerTracker";
-
- // Keep track of which pointers sent to the system are down.
- private int mInjectedPointersDown;
-
- // The time of the last injected down.
- private long mLastInjectedDownEventTime;
-
- // The last injected hover event.
- private MotionEvent mLastInjectedHoverEvent;
-
- /**
- * Processes an injected {@link MotionEvent} event.
- *
- * @param event The event to process.
- */
- public void onMotionEvent(MotionEvent event) {
- final int action = event.getActionMasked();
- final int pointerId = event.getPointerId(event.getActionIndex());
- final int pointerFlag = (1 << pointerId);
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- case MotionEvent.ACTION_POINTER_DOWN:
- mInjectedPointersDown |= pointerFlag;
- mLastInjectedDownEventTime = event.getDownTime();
- break;
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_POINTER_UP:
- mInjectedPointersDown &= ~pointerFlag;
- if (mInjectedPointersDown == 0) {
- mLastInjectedDownEventTime = 0;
- }
- break;
- case MotionEvent.ACTION_HOVER_ENTER:
- case MotionEvent.ACTION_HOVER_MOVE:
- case MotionEvent.ACTION_HOVER_EXIT:
- if (mLastInjectedHoverEvent != null) {
- mLastInjectedHoverEvent.recycle();
- }
- mLastInjectedHoverEvent = MotionEvent.obtain(event);
- break;
- }
- if (DEBUG) {
- Slog.i(LOG_TAG_INJECTED_POINTER_TRACKER, "Injected pointer:\n" + toString());
- }
- }
-
- /** Clears the internals state. */
- public void clear() {
- mInjectedPointersDown = 0;
- }
-
- /** @return The time of the last injected down event. */
- public long getLastInjectedDownEventTime() {
- return mLastInjectedDownEventTime;
- }
-
- /** @return The number of down pointers injected to the view hierarchy. */
- public int getInjectedPointerDownCount() {
- return Integer.bitCount(mInjectedPointersDown);
- }
-
- /** @return The bits of the injected pointers that are down. */
- public int getInjectedPointersDown() {
- return mInjectedPointersDown;
- }
-
- /**
- * Whether an injected pointer is down.
- *
- * @param pointerId The unique pointer id.
- * @return True if the pointer is down.
- */
- public boolean isInjectedPointerDown(int pointerId) {
- final int pointerFlag = (1 << pointerId);
- return (mInjectedPointersDown & pointerFlag) != 0;
- }
-
- /** @return The the last injected hover event. */
- public MotionEvent getLastInjectedHoverEvent() {
- return mLastInjectedHoverEvent;
- }
-
- @Override
- public String toString() {
- StringBuilder builder = new StringBuilder();
- builder.append("=========================");
- builder.append("\nDown pointers #");
- builder.append(Integer.bitCount(mInjectedPointersDown));
- builder.append(" [ ");
- for (int i = 0; i < MAX_POINTER_COUNT; i++) {
- if ((mInjectedPointersDown & i) != 0) {
- builder.append(i);
- builder.append(" ");
- }
- }
- builder.append("]");
- builder.append("\n=========================");
- return builder.toString();
- }
- }
/** This class tracks where and when a pointer went down. It does not track its movement. */
class ReceivedPointerTracker {
private static final String LOG_TAG_RECEIVED_POINTER_TRACKER = "ReceivedPointerTracker";
diff --git a/services/backup/Android.bp b/services/backup/Android.bp
index ef03d83d4916..a3b0c891d00a 100644
--- a/services/backup/Android.bp
+++ b/services/backup/Android.bp
@@ -2,4 +2,5 @@ java_library_static {
name: "services.backup",
srcs: ["java/**/*.java"],
libs: ["services.core"],
+ static_libs: ["backuplib"],
}
diff --git a/services/backup/backuplib/Android.bp b/services/backup/backuplib/Android.bp
new file mode 100644
index 000000000000..7b194a0923c2
--- /dev/null
+++ b/services/backup/backuplib/Android.bp
@@ -0,0 +1,5 @@
+java_library {
+ name: "backuplib",
+ srcs: ["java/**/*.java"],
+ libs: ["services.core"],
+}
diff --git a/services/backup/java/com/android/server/backup/TransportManager.java b/services/backup/backuplib/java/com/android/server/backup/TransportManager.java
index 30ce4cf2fd3f..30ce4cf2fd3f 100644
--- a/services/backup/java/com/android/server/backup/TransportManager.java
+++ b/services/backup/backuplib/java/com/android/server/backup/TransportManager.java
diff --git a/services/backup/java/com/android/server/backup/transport/OnTransportRegisteredListener.java b/services/backup/backuplib/java/com/android/server/backup/transport/OnTransportRegisteredListener.java
index 391ec2d7f294..391ec2d7f294 100644
--- a/services/backup/java/com/android/server/backup/transport/OnTransportRegisteredListener.java
+++ b/services/backup/backuplib/java/com/android/server/backup/transport/OnTransportRegisteredListener.java
diff --git a/services/backup/java/com/android/server/backup/transport/TransportClient.java b/services/backup/backuplib/java/com/android/server/backup/transport/TransportClient.java
index 7c5a57c004e4..7c5a57c004e4 100644
--- a/services/backup/java/com/android/server/backup/transport/TransportClient.java
+++ b/services/backup/backuplib/java/com/android/server/backup/transport/TransportClient.java
diff --git a/services/backup/java/com/android/server/backup/transport/TransportClientManager.java b/services/backup/backuplib/java/com/android/server/backup/transport/TransportClientManager.java
index a4e9b1091bed..a4e9b1091bed 100644
--- a/services/backup/java/com/android/server/backup/transport/TransportClientManager.java
+++ b/services/backup/backuplib/java/com/android/server/backup/transport/TransportClientManager.java
diff --git a/services/backup/java/com/android/server/backup/transport/TransportConnectionListener.java b/services/backup/backuplib/java/com/android/server/backup/transport/TransportConnectionListener.java
index 1ccffd01d12c..1ccffd01d12c 100644
--- a/services/backup/java/com/android/server/backup/transport/TransportConnectionListener.java
+++ b/services/backup/backuplib/java/com/android/server/backup/transport/TransportConnectionListener.java
diff --git a/services/backup/java/com/android/server/backup/transport/TransportNotAvailableException.java b/services/backup/backuplib/java/com/android/server/backup/transport/TransportNotAvailableException.java
index c08eb7f4a54e..c08eb7f4a54e 100644
--- a/services/backup/java/com/android/server/backup/transport/TransportNotAvailableException.java
+++ b/services/backup/backuplib/java/com/android/server/backup/transport/TransportNotAvailableException.java
diff --git a/services/backup/java/com/android/server/backup/transport/TransportNotRegisteredException.java b/services/backup/backuplib/java/com/android/server/backup/transport/TransportNotRegisteredException.java
index 02766deeb7e2..02766deeb7e2 100644
--- a/services/backup/java/com/android/server/backup/transport/TransportNotRegisteredException.java
+++ b/services/backup/backuplib/java/com/android/server/backup/transport/TransportNotRegisteredException.java
diff --git a/services/backup/java/com/android/server/backup/transport/TransportStats.java b/services/backup/backuplib/java/com/android/server/backup/transport/TransportStats.java
index bd84782122ad..bd84782122ad 100644
--- a/services/backup/java/com/android/server/backup/transport/TransportStats.java
+++ b/services/backup/backuplib/java/com/android/server/backup/transport/TransportStats.java
diff --git a/services/backup/java/com/android/server/backup/transport/TransportUtils.java b/services/backup/backuplib/java/com/android/server/backup/transport/TransportUtils.java
index 766d77bd639c..766d77bd639c 100644
--- a/services/backup/java/com/android/server/backup/transport/TransportUtils.java
+++ b/services/backup/backuplib/java/com/android/server/backup/transport/TransportUtils.java
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/MountServiceIdler.java b/services/core/java/com/android/server/MountServiceIdler.java
index 79b1226a3f7e..6bc1a570b7c0 100644
--- a/services/core/java/com/android/server/MountServiceIdler.java
+++ b/services/core/java/com/android/server/MountServiceIdler.java
@@ -99,32 +99,28 @@ public class MountServiceIdler extends JobService {
public static void scheduleIdlePass(Context context) {
JobScheduler tm = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
- final long today3AM = MidnightInTime(0, 0).getTimeInMillis();
- final long today4AM = MidnightInTime(0, 1).getTimeInMillis();
+ final long today3AM = offsetFromTodayMidnight(0, 3).getTimeInMillis();
+ final long today4AM = offsetFromTodayMidnight(0, 4).getTimeInMillis();
+ final long tomorrow3AM = offsetFromTodayMidnight(1, 3).getTimeInMillis();
- long nextScheduleTime, maxScheduleTime;
+ long nextScheduleTime;
if (System.currentTimeMillis() > today3AM && System.currentTimeMillis() < today4AM) {
nextScheduleTime = TimeUnit.SECONDS.toMillis(10);
- maxScheduleTime = today4AM - System.currentTimeMillis();
} else {
- final long tomorrow3AM = MidnightInTime(1, 0).getTimeInMillis();
- final long twodays3AM = MidnightInTime(2, 0).getTimeInMillis();
nextScheduleTime = tomorrow3AM - System.currentTimeMillis(); // 3AM tomorrow
- maxScheduleTime = twodays3AM - System.currentTimeMillis(); // 3AM in two days
}
JobInfo.Builder builder = new JobInfo.Builder(MOUNT_JOB_ID, sIdleService);
builder.setRequiresDeviceIdle(true);
- builder.setRequiresCharging(true);
+ builder.setRequiresBatteryNotLow(true);
builder.setMinimumLatency(nextScheduleTime);
- builder.setOverrideDeadline(maxScheduleTime);
tm.schedule(builder.build());
}
- private static Calendar MidnightInTime(int nDays, int nHours) {
+ private static Calendar offsetFromTodayMidnight(int nDays, int nHours) {
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
- calendar.set(Calendar.HOUR_OF_DAY, 3 + nHours);
+ calendar.set(Calendar.HOUR_OF_DAY, nHours);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index 20a9b20dd385..bc509561163a 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;
@@ -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<>();
@@ -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(
@@ -805,7 +812,6 @@ public class PackageWatchdog {
*/
private static class ObserverInternal {
public final String name;
- //TODO(b/120598832): Add getter for mPackages
@GuardedBy("mLock")
public final ArrayMap<String, MonitoredPackage> packages = new ArrayMap<>();
@Nullable
@@ -967,6 +973,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
@@ -986,12 +995,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);
@@ -1026,20 +1029,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/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 1675b94292a7..e7569bee239e 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1748,8 +1748,8 @@ public final class ActiveServices {
s.instanceName, s.processName);
// Once the apps have become associated, if one of them is caller is ephemeral
// the target app should now be able to see the calling app
- mAm.grantEphemeralAccessLocked(callerApp.userId, service,
- UserHandle.getAppId(s.appInfo.uid), UserHandle.getAppId(callerApp.uid));
+ mAm.grantImplicitAccess(callerApp.userId, service,
+ UserHandle.getAppId(callerApp.uid), UserHandle.getAppId(s.appInfo.uid));
AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
ConnectionRecord c = new ConnectionRecord(b, activity,
@@ -2802,8 +2802,9 @@ public final class ActiveServices {
mAm.mUgmInternal.grantUriPermissionUncheckedFromIntent(si.neededGrants,
si.getUriPermissionsLocked());
}
- mAm.grantEphemeralAccessLocked(r.userId, si.intent, UserHandle.getAppId(r.appInfo.uid),
- UserHandle.getAppId(si.callingId));
+ mAm.grantImplicitAccess(r.userId, si.intent, UserHandle.getAppId(si.callingId),
+ UserHandle.getAppId(r.appInfo.uid)
+ );
bumpServiceExecutingLocked(r, execInFg, "start");
if (!oomAdjusted) {
oomAdjusted = true;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 09bfb7a1adca..3c7cb88174e1 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5265,11 +5265,14 @@ public class ActivityManagerService extends IActivityManager.Stub
// Inform checkpointing systems of success
try {
+ // This line is needed to CTS test for the correct exception handling
+ // See b/138952436#comment36 for context
+ Slog.i(TAG, "About to commit checkpoint");
IStorageManager storageManager = PackageHelper.getStorageManager();
storageManager.commitChanges();
} catch (Exception e) {
PowerManager pm = (PowerManager)
- mContext.getSystemService(Context.POWER_SERVICE);
+ mInjector.getContext().getSystemService(Context.POWER_SERVICE);
pm.reboot("Checkpoint commit failed");
}
@@ -6115,10 +6118,9 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@VisibleForTesting
- public void grantEphemeralAccessLocked(int userId, Intent intent,
- int targetAppId, int ephemeralAppId) {
+ public void grantImplicitAccess(int userId, Intent intent, int callingAppId, int targetAppId) {
getPackageManagerInternalLocked().
- grantEphemeralAccess(userId, intent, targetAppId, ephemeralAppId);
+ grantImplicitAccess(userId, intent, callingAppId, targetAppId);
}
/**
@@ -7088,9 +7090,10 @@ public class ActivityManagerService extends IActivityManager.Stub
}
checkTime(startTime, "getContentProviderImpl: done!");
- grantEphemeralAccessLocked(userId, null /*intent*/,
- UserHandle.getAppId(cpi.applicationInfo.uid),
- UserHandle.getAppId(Binder.getCallingUid()));
+ grantImplicitAccess(userId, null /*intent*/,
+ UserHandle.getAppId(Binder.getCallingUid()),
+ UserHandle.getAppId(cpi.applicationInfo.uid)
+ );
}
// Wait for the provider to be published...
@@ -15262,7 +15265,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 6666cf42ab5d..fd64df9e86e0 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+)");
@@ -70,8 +63,6 @@ public final class MemoryStatUtil {
private static final Pattern CACHE_IN_BYTES = Pattern.compile("total_cache (\\d+)");
private static final Pattern SWAP_IN_BYTES = Pattern.compile("total_swap (\\d+)");
- private static final Pattern RSS_HIGH_WATERMARK_IN_KILOBYTES =
- Pattern.compile("VmHWM:\\s*(\\d+)\\s*kB");
private static final Pattern PROCFS_RSS_IN_KILOBYTES =
Pattern.compile("VmRSS:\\s*(\\d+)\\s*kB");
private static final Pattern PROCFS_ANON_RSS_IN_KILOBYTES =
@@ -79,11 +70,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;
@@ -125,15 +111,6 @@ 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 kilobytes or 0 if not available.
- */
- public static int readRssHighWaterMarkFromProcfs(int pid) {
- final String statusPath = String.format(Locale.US, PROC_STATUS_FILE_FMT, pid);
- return parseVmHWMFromProcfs(readFileContents(statusPath));
- }
-
- /**
* Reads cmdline of a process from procfs.
*
* Returns content of /proc/pid/cmdline (e.g. /system/bin/statsd) or an empty string
@@ -144,26 +121,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,19 +193,6 @@ public final class MemoryStatUtil {
}
/**
- * 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 int parseVmHWMFromProcfs(String procStatusContents) {
- if (procStatusContents == null || procStatusContents.isEmpty()) {
- return 0;
- }
- return (int) tryParseLong(RSS_HIGH_WATERMARK_IN_KILOBYTES, procStatusContents);
- }
-
-
- /**
* Parses cmdline out of the contents of the /proc/pid/cmdline file in procfs.
*
* Parsing is required to strip anything after first null byte.
@@ -266,55 +210,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() {
@@ -351,40 +246,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/am/OWNERS b/services/core/java/com/android/server/am/OWNERS
index 0509f9f0ec11..2f9a5c952659 100644
--- a/services/core/java/com/android/server/am/OWNERS
+++ b/services/core/java/com/android/server/am/OWNERS
@@ -12,6 +12,7 @@ suprabh@google.com
varunshah@google.com
kwekua@google.com
bookatz@google.com
+jji@google.com
# Windows & Activities
ogunwale@google.com
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 7f69a683b18a..159e5b87e5a8 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -608,6 +608,10 @@ public class AppOpsService extends IAppOpsService.Stub {
private void updateProxyState(long key, int proxyUid,
@Nullable String proxyPackageName) {
+ if (proxyUid == Process.INVALID_UID) {
+ return;
+ }
+
if (mProxyUids == null) {
mProxyUids = new LongSparseLongArray();
}
@@ -882,20 +886,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);
}
}
@@ -2852,9 +2856,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,
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 6010b1dc88c4..e8198b917c08 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -57,13 +57,6 @@ import java.io.PrintWriter;
private final @NonNull AudioService mAudioService;
private final @NonNull Context mContext;
- /** Forced device usage for communications sent to AudioSystem */
- private int mForcedUseForComm;
- /**
- * Externally reported force device usage state returned by getters: always consistent
- * with requests by setters */
- private int mForcedUseForCommExt;
-
// Manages all connected devices, only ever accessed on the message loop
private final AudioDeviceInventory mDeviceInventory;
// Manages notifications to BT service
@@ -71,24 +64,34 @@ import java.io.PrintWriter;
//-------------------------------------------------------------------
- // we use a different lock than mDeviceStateLock so as not to create
- // lock contention between enqueueing a message and handling them
- private static final Object sLastDeviceConnectionMsgTimeLock = new Object();
- @GuardedBy("sLastDeviceConnectionMsgTimeLock")
+ /**
+ * Lock to guard:
+ * - any changes to the message queue: enqueueing or removing any message
+ * - state of A2DP enabled
+ * - force use for communication + SCO changes
+ */
+ private final Object mDeviceBrokerLock = new Object();
+
+ @GuardedBy("mDeviceBrokerLock")
private static long sLastDeviceConnectMsgTime = 0;
- // General lock to be taken whenever the state of the audio devices is to be checked or changed
- private final Object mDeviceStateLock = new Object();
- // Request to override default use of A2DP for media.
- @GuardedBy("mDeviceStateLock")
+ /** Request to override default use of A2DP for media */
+ @GuardedBy("mDeviceBrokerLock")
private boolean mBluetoothA2dpEnabled;
- // lock always taken when accessing AudioService.mSetModeDeathHandlers
- // TODO do not "share" the lock between AudioService and BtHelpr, see b/123769055
- /*package*/ final Object mSetModeLock = new Object();
+ /** Forced device usage for communications sent to AudioSystem */
+ @GuardedBy("mDeviceBrokerLock")
+ private int mForcedUseForComm;
+ /**
+ * Externally reported force device usage state returned by getters: always consistent
+ * with requests by setters */
+ @GuardedBy("mDeviceBrokerLock")
+ private int mForcedUseForCommExt;
+
//-------------------------------------------------------------------
+ /** Normal constructor used by AudioService */
/*package*/ AudioDeviceBroker(@NonNull Context context, @NonNull AudioService service) {
mContext = context;
mAudioService = service;
@@ -127,36 +130,37 @@ import java.io.PrintWriter;
// All post* methods are asynchronous
/*package*/ void onSystemReady() {
- synchronized (mSetModeLock) {
- synchronized (mDeviceStateLock) {
- mBtHelper.onSystemReady();
- }
- }
+ mBtHelper.onSystemReady();
}
/*package*/ void onAudioServerDied() {
// Restore forced usage for communications and record
- synchronized (mDeviceStateLock) {
+ synchronized (mDeviceBrokerLock) {
AudioSystem.setParameters(
"BT_SCO=" + (mForcedUseForComm == AudioSystem.FORCE_BT_SCO ? "on" : "off"));
onSetForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, "onAudioServerDied");
onSetForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm, "onAudioServerDied");
+
+ // restore devices
+ sendMsgNoDelay(MSG_RESTORE_DEVICES, SENDMSG_REPLACE);
}
- // restore devices
- sendMsgNoDelay(MSG_RESTORE_DEVICES, SENDMSG_REPLACE);
}
/*package*/ void setForceUse_Async(int useCase, int config, String eventSource) {
- sendIILMsgNoDelay(MSG_IIL_SET_FORCE_USE, SENDMSG_QUEUE,
- useCase, config, eventSource);
+ synchronized (mDeviceBrokerLock) {
+ sendIILMsgNoDelay(MSG_IIL_SET_FORCE_USE, SENDMSG_QUEUE,
+ useCase, config, eventSource);
+ }
}
/*package*/ void toggleHdmiIfConnected_Async() {
- sendMsgNoDelay(MSG_TOGGLE_HDMI, SENDMSG_QUEUE);
+ synchronized (mDeviceBrokerLock) {
+ sendMsgNoDelay(MSG_TOGGLE_HDMI, SENDMSG_QUEUE);
+ }
}
/*package*/ void disconnectAllBluetoothProfiles() {
- synchronized (mDeviceStateLock) {
+ synchronized (mDeviceBrokerLock) {
mBtHelper.disconnectAllBluetoothProfiles();
}
}
@@ -168,15 +172,11 @@ import java.io.PrintWriter;
* @param intent
*/
/*package*/ void receiveBtEvent(@NonNull Intent intent) {
- synchronized (mSetModeLock) {
- synchronized (mDeviceStateLock) {
- mBtHelper.receiveBtEvent(intent);
- }
- }
+ mBtHelper.receiveBtEvent(intent);
}
/*package*/ void setBluetoothA2dpOn_Async(boolean on, String source) {
- synchronized (mDeviceStateLock) {
+ synchronized (mDeviceBrokerLock) {
if (mBluetoothA2dpEnabled == on) {
return;
}
@@ -196,7 +196,7 @@ import java.io.PrintWriter;
* @return true if speakerphone state changed
*/
/*package*/ boolean setSpeakerphoneOn(boolean on, String eventSource) {
- synchronized (mDeviceStateLock) {
+ synchronized (mDeviceBrokerLock) {
final boolean wasOn = isSpeakerphoneOn();
if (on) {
if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {
@@ -214,7 +214,7 @@ import java.io.PrintWriter;
}
/*package*/ boolean isSpeakerphoneOn() {
- synchronized (mDeviceStateLock) {
+ synchronized (mDeviceBrokerLock) {
return (mForcedUseForCommExt == AudioSystem.FORCE_SPEAKER);
}
}
@@ -223,9 +223,7 @@ import java.io.PrintWriter;
@AudioService.ConnectionState int state, String address, String name,
String caller) {
//TODO move logging here just like in setBluetooth* methods
- synchronized (mDeviceStateLock) {
- mDeviceInventory.setWiredDeviceConnectionState(type, state, address, name, caller);
- }
+ mDeviceInventory.setWiredDeviceConnectionState(type, state, address, name, caller);
}
private static final class BtDeviceConnectionInfo {
@@ -259,27 +257,24 @@ import java.io.PrintWriter;
final BtDeviceConnectionInfo info = new BtDeviceConnectionInfo(device, state, profile,
suppressNoisyIntent, a2dpVolume);
- // when receiving a request to change the connection state of a device, this last request
- // is the source of truth, so cancel all previous requests
- removeAllA2dpConnectionEvents(device);
-
- sendLMsgNoDelay(
- state == BluetoothProfile.STATE_CONNECTED
- ? MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION
- : MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION,
- SENDMSG_QUEUE, info);
- }
-
- /** remove all previously scheduled connection and disconnection events for the given device */
- private void removeAllA2dpConnectionEvents(@NonNull BluetoothDevice device) {
- mBrokerHandler.removeMessages(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION,
- device);
- mBrokerHandler.removeMessages(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION,
- device);
- mBrokerHandler.removeMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED,
- device);
- mBrokerHandler.removeMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED,
- device);
+ synchronized (mDeviceBrokerLock) {
+ // when receiving a request to change the connection state of a device, this last
+ // request is the source of truth, so cancel all previous requests
+ mBrokerHandler.removeMessages(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION,
+ device);
+ mBrokerHandler.removeMessages(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION,
+ device);
+ mBrokerHandler.removeMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED,
+ device);
+ mBrokerHandler.removeMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED,
+ device);
+
+ sendLMsgNoDelay(
+ state == BluetoothProfile.STATE_CONNECTED
+ ? MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION
+ : MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION,
+ SENDMSG_QUEUE, info);
+ }
}
private static final class HearingAidDeviceConnectionInfo {
@@ -305,25 +300,27 @@ import java.io.PrintWriter;
boolean suppressNoisyIntent, int musicDevice, @NonNull String eventSource) {
final HearingAidDeviceConnectionInfo info = new HearingAidDeviceConnectionInfo(
device, state, suppressNoisyIntent, musicDevice, eventSource);
- sendLMsgNoDelay(MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT, SENDMSG_QUEUE, info);
+ synchronized (mDeviceBrokerLock) {
+ sendLMsgNoDelay(MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT, SENDMSG_QUEUE, info);
+ }
}
// never called by system components
/*package*/ void setBluetoothScoOnByApp(boolean on) {
- synchronized (mDeviceStateLock) {
+ synchronized (mDeviceBrokerLock) {
mForcedUseForCommExt = on ? AudioSystem.FORCE_BT_SCO : AudioSystem.FORCE_NONE;
}
}
/*package*/ boolean isBluetoothScoOnForApp() {
- synchronized (mDeviceStateLock) {
+ synchronized (mDeviceBrokerLock) {
return mForcedUseForCommExt == AudioSystem.FORCE_BT_SCO;
}
}
/*package*/ void setBluetoothScoOn(boolean on, String eventSource) {
//Log.i(TAG, "setBluetoothScoOnInt: " + on + " " + eventSource);
- synchronized (mDeviceStateLock) {
+ synchronized (mDeviceBrokerLock) {
if (on) {
// do not accept SCO ON if SCO audio is not connected
if (!mBtHelper.isBluetoothScoOn()) {
@@ -346,58 +343,55 @@ import java.io.PrintWriter;
}
/*package*/ AudioRoutesInfo startWatchingRoutes(IAudioRoutesObserver observer) {
- synchronized (mDeviceStateLock) {
- return mDeviceInventory.startWatchingRoutes(observer);
- }
+ return mDeviceInventory.startWatchingRoutes(observer);
+
}
/*package*/ AudioRoutesInfo getCurAudioRoutes() {
- synchronized (mDeviceStateLock) {
- return mDeviceInventory.getCurAudioRoutes();
- }
+ return mDeviceInventory.getCurAudioRoutes();
}
/*package*/ boolean isAvrcpAbsoluteVolumeSupported() {
- synchronized (mDeviceStateLock) {
- return mBtHelper.isAvrcpAbsoluteVolumeSupported();
- }
+ return mBtHelper.isAvrcpAbsoluteVolumeSupported();
}
/*package*/ boolean isBluetoothA2dpOn() {
- synchronized (mDeviceStateLock) {
+ synchronized (mDeviceBrokerLock) {
return mBluetoothA2dpEnabled;
}
}
/*package*/ void postSetAvrcpAbsoluteVolumeIndex(int index) {
- sendIMsgNoDelay(MSG_I_SET_AVRCP_ABSOLUTE_VOLUME, SENDMSG_REPLACE, index);
+ synchronized (mDeviceBrokerLock) {
+ sendIMsgNoDelay(MSG_I_SET_AVRCP_ABSOLUTE_VOLUME, SENDMSG_REPLACE, index);
+ }
}
/*package*/ void postSetHearingAidVolumeIndex(int index, int streamType) {
- sendIIMsgNoDelay(MSG_II_SET_HEARING_AID_VOLUME, SENDMSG_REPLACE, index, streamType);
+ synchronized (mDeviceBrokerLock) {
+ sendIIMsgNoDelay(MSG_II_SET_HEARING_AID_VOLUME, SENDMSG_REPLACE, index, streamType);
+ }
}
/*package*/ void postDisconnectBluetoothSco(int exceptPid) {
- sendIMsgNoDelay(MSG_I_DISCONNECT_BT_SCO, SENDMSG_REPLACE, exceptPid);
+ synchronized (mDeviceBrokerLock) {
+ sendIMsgNoDelay(MSG_I_DISCONNECT_BT_SCO, SENDMSG_REPLACE, exceptPid);
+ }
}
/*package*/ void postBluetoothA2dpDeviceConfigChange(@NonNull BluetoothDevice device) {
- sendLMsgNoDelay(MSG_L_A2DP_DEVICE_CONFIG_CHANGE, SENDMSG_QUEUE, device);
+ synchronized (mDeviceBrokerLock) {
+ sendLMsgNoDelay(MSG_L_A2DP_DEVICE_CONFIG_CHANGE, SENDMSG_QUEUE, device);
+ }
}
- @GuardedBy("mSetModeLock")
/*package*/ void startBluetoothScoForClient_Sync(IBinder cb, int scoAudioMode,
@NonNull String eventSource) {
- synchronized (mDeviceStateLock) {
- mBtHelper.startBluetoothScoForClient(cb, scoAudioMode, eventSource);
- }
+ mBtHelper.startBluetoothScoForClient(cb, scoAudioMode, eventSource);
}
- @GuardedBy("mSetModeLock")
/*package*/ void stopBluetoothScoForClient_Sync(IBinder cb, @NonNull String eventSource) {
- synchronized (mDeviceStateLock) {
- mBtHelper.stopBluetoothScoForClient(cb, eventSource);
- }
+ mBtHelper.stopBluetoothScoForClient(cb, eventSource);
}
//---------------------------------------------------------------------
@@ -460,77 +454,109 @@ import java.io.PrintWriter;
//---------------------------------------------------------------------
// Message handling on behalf of helper classes
/*package*/ void postBroadcastScoConnectionState(int state) {
- sendIMsgNoDelay(MSG_I_BROADCAST_BT_CONNECTION_STATE, SENDMSG_QUEUE, state);
+ synchronized (mDeviceBrokerLock) {
+ sendIMsgNoDelay(MSG_I_BROADCAST_BT_CONNECTION_STATE, SENDMSG_QUEUE, state);
+ }
}
/*package*/ void postBroadcastBecomingNoisy() {
- sendMsgNoDelay(MSG_BROADCAST_AUDIO_BECOMING_NOISY, SENDMSG_REPLACE);
+ synchronized (mDeviceBrokerLock) {
+ sendMsgNoDelay(MSG_BROADCAST_AUDIO_BECOMING_NOISY, SENDMSG_REPLACE);
+ }
}
/*package*/ void postA2dpSinkConnection(@AudioService.BtProfileConnectionState int state,
@NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo, int delay) {
- sendILMsg(state == BluetoothA2dp.STATE_CONNECTED
- ? MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED
- : MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED,
- SENDMSG_QUEUE,
- state, btDeviceInfo, delay);
+ synchronized (mDeviceBrokerLock) {
+ sendILMsg(state == BluetoothA2dp.STATE_CONNECTED
+ ? MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED
+ : MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED,
+ SENDMSG_QUEUE,
+ state, btDeviceInfo, delay);
+ }
}
/*package*/ void postA2dpSourceConnection(@AudioService.BtProfileConnectionState int state,
@NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo, int delay) {
- sendILMsg(MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE, SENDMSG_QUEUE,
- state, btDeviceInfo, delay);
+ synchronized (mDeviceBrokerLock) {
+ sendILMsg(MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE, SENDMSG_QUEUE,
+ state, btDeviceInfo, delay);
+ }
}
/*package*/ void postSetWiredDeviceConnectionState(
AudioDeviceInventory.WiredDeviceConnectionState connectionState, int delay) {
- sendLMsg(MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE, SENDMSG_QUEUE, connectionState, delay);
+ synchronized (mDeviceBrokerLock) {
+ sendLMsg(MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE, SENDMSG_QUEUE,
+ connectionState, delay);
+ }
}
/*package*/ void postSetHearingAidConnectionState(
@AudioService.BtProfileConnectionState int state,
@NonNull BluetoothDevice device, int delay) {
- sendILMsg(MSG_IL_SET_HEARING_AID_CONNECTION_STATE, SENDMSG_QUEUE,
- state,
- device,
- delay);
+ synchronized (mDeviceBrokerLock) {
+ sendILMsg(MSG_IL_SET_HEARING_AID_CONNECTION_STATE, SENDMSG_QUEUE,
+ state,
+ device,
+ delay);
+ }
}
/*package*/ void postDisconnectA2dp() {
- sendMsgNoDelay(MSG_DISCONNECT_A2DP, SENDMSG_QUEUE);
+ synchronized (mDeviceBrokerLock) {
+ sendMsgNoDelay(MSG_DISCONNECT_A2DP, SENDMSG_QUEUE);
+ }
}
/*package*/ void postDisconnectA2dpSink() {
- sendMsgNoDelay(MSG_DISCONNECT_A2DP_SINK, SENDMSG_QUEUE);
+ synchronized (mDeviceBrokerLock) {
+ sendMsgNoDelay(MSG_DISCONNECT_A2DP_SINK, SENDMSG_QUEUE);
+ }
}
/*package*/ void postDisconnectHearingAid() {
- sendMsgNoDelay(MSG_DISCONNECT_BT_HEARING_AID, SENDMSG_QUEUE);
+ synchronized (mDeviceBrokerLock) {
+ sendMsgNoDelay(MSG_DISCONNECT_BT_HEARING_AID, SENDMSG_QUEUE);
+ }
}
/*package*/ void postDisconnectHeadset() {
- sendMsgNoDelay(MSG_DISCONNECT_BT_HEADSET, SENDMSG_QUEUE);
+ synchronized (mDeviceBrokerLock) {
+ sendMsgNoDelay(MSG_DISCONNECT_BT_HEADSET, SENDMSG_QUEUE);
+ }
}
/*package*/ void postBtA2dpProfileConnected(BluetoothA2dp a2dpProfile) {
- sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP, SENDMSG_QUEUE, a2dpProfile);
+ synchronized (mDeviceBrokerLock) {
+ sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP, SENDMSG_QUEUE, a2dpProfile);
+ }
}
/*package*/ void postBtA2dpSinkProfileConnected(BluetoothProfile profile) {
- sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP_SINK, SENDMSG_QUEUE, profile);
+ synchronized (mDeviceBrokerLock) {
+ sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP_SINK, SENDMSG_QUEUE, profile);
+ }
}
/*package*/ void postBtHeasetProfileConnected(BluetoothHeadset headsetProfile) {
- sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEADSET, SENDMSG_QUEUE, headsetProfile);
+ synchronized (mDeviceBrokerLock) {
+ sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEADSET, SENDMSG_QUEUE,
+ headsetProfile);
+ }
}
/*package*/ void postBtHearingAidProfileConnected(BluetoothHearingAid hearingAidProfile) {
- sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEARING_AID, SENDMSG_QUEUE,
- hearingAidProfile);
+ synchronized (mDeviceBrokerLock) {
+ sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEARING_AID, SENDMSG_QUEUE,
+ hearingAidProfile);
+ }
}
/*package*/ void postScoClientDied(Object obj) {
- sendLMsgNoDelay(MSG_L_SCOCLIENT_DIED, SENDMSG_QUEUE, obj);
+ synchronized (mDeviceBrokerLock) {
+ sendLMsgNoDelay(MSG_L_SCOCLIENT_DIED, SENDMSG_QUEUE, obj);
+ }
}
//---------------------------------------------------------------------
@@ -545,7 +571,7 @@ import java.io.PrintWriter;
.append(") from u/pid:").append(Binder.getCallingUid()).append("/")
.append(Binder.getCallingPid()).append(" src:").append(source).toString();
- synchronized (mDeviceStateLock) {
+ synchronized (mDeviceBrokerLock) {
mBluetoothA2dpEnabled = on;
mBrokerHandler.removeMessages(MSG_IIL_SET_FORCE_BT_A2DP_USE);
onSetForceUse(
@@ -557,71 +583,85 @@ import java.io.PrintWriter;
/*package*/ boolean handleDeviceConnection(boolean connect, int device, String address,
String deviceName) {
- synchronized (mDeviceStateLock) {
- return mDeviceInventory.handleDeviceConnection(connect, device, address, deviceName);
- }
+ return mDeviceInventory.handleDeviceConnection(connect, device, address, deviceName);
}
/*package*/ void postSetA2dpSourceConnectionState(@BluetoothProfile.BtProfileState int state,
@NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo) {
final int intState = (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0;
- sendILMsgNoDelay(MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE, SENDMSG_QUEUE, state,
- btDeviceInfo);
+ synchronized (mDeviceBrokerLock) {
+ sendILMsgNoDelay(MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE, SENDMSG_QUEUE, state,
+ btDeviceInfo);
+ }
}
/*package*/ void handleFailureToConnectToBtHeadsetService(int delay) {
- sendMsg(MSG_BT_HEADSET_CNCT_FAILED, SENDMSG_REPLACE, delay);
+ synchronized (mDeviceBrokerLock) {
+ sendMsg(MSG_BT_HEADSET_CNCT_FAILED, SENDMSG_REPLACE, delay);
+ }
}
/*package*/ void handleCancelFailureToConnectToBtHeadsetService() {
- mBrokerHandler.removeMessages(MSG_BT_HEADSET_CNCT_FAILED);
+ synchronized (mDeviceBrokerLock) {
+ mBrokerHandler.removeMessages(MSG_BT_HEADSET_CNCT_FAILED);
+ }
}
/*package*/ void postReportNewRoutes() {
- sendMsgNoDelay(MSG_REPORT_NEW_ROUTES, SENDMSG_NOOP);
+ synchronized (mDeviceBrokerLock) {
+ sendMsgNoDelay(MSG_REPORT_NEW_ROUTES, SENDMSG_NOOP);
+ }
}
/*package*/ void cancelA2dpDockTimeout() {
- mBrokerHandler.removeMessages(MSG_IL_BTA2DP_DOCK_TIMEOUT);
+ synchronized (mDeviceBrokerLock) {
+ mBrokerHandler.removeMessages(MSG_IL_BTA2DP_DOCK_TIMEOUT);
+ }
}
+ // FIXME: used by?
/*package*/ void postA2dpActiveDeviceChange(
@NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo) {
- sendLMsgNoDelay(MSG_L_A2DP_ACTIVE_DEVICE_CHANGE, SENDMSG_QUEUE, btDeviceInfo);
+ synchronized (mDeviceBrokerLock) {
+ sendLMsgNoDelay(MSG_L_A2DP_ACTIVE_DEVICE_CHANGE, SENDMSG_QUEUE, btDeviceInfo);
+ }
}
/*package*/ boolean hasScheduledA2dpDockTimeout() {
- return mBrokerHandler.hasMessages(MSG_IL_BTA2DP_DOCK_TIMEOUT);
+ synchronized (mDeviceBrokerLock) {
+ return mBrokerHandler.hasMessages(MSG_IL_BTA2DP_DOCK_TIMEOUT);
+ }
}
// must be called synchronized on mConnectedDevices
/*package*/ boolean hasScheduledA2dpSinkConnectionState(BluetoothDevice btDevice) {
- return (mBrokerHandler.hasMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED,
- new BtHelper.BluetoothA2dpDeviceInfo(btDevice))
- || mBrokerHandler.hasMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED,
- new BtHelper.BluetoothA2dpDeviceInfo(btDevice)));
+ synchronized (mDeviceBrokerLock) {
+ return (mBrokerHandler.hasMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED,
+ new BtHelper.BluetoothA2dpDeviceInfo(btDevice))
+ || mBrokerHandler.hasMessages(
+ MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED,
+ new BtHelper.BluetoothA2dpDeviceInfo(btDevice)));
+ }
}
/*package*/ void setA2dpDockTimeout(String address, int a2dpCodec, int delayMs) {
- sendILMsg(MSG_IL_BTA2DP_DOCK_TIMEOUT, SENDMSG_QUEUE, a2dpCodec, address, delayMs);
+ synchronized (mDeviceBrokerLock) {
+ sendILMsg(MSG_IL_BTA2DP_DOCK_TIMEOUT, SENDMSG_QUEUE, a2dpCodec, address, delayMs);
+ }
}
/*package*/ void setAvrcpAbsoluteVolumeSupported(boolean supported) {
- synchronized (mDeviceStateLock) {
- mBtHelper.setAvrcpAbsoluteVolumeSupported(supported);
- }
+ mBtHelper.setAvrcpAbsoluteVolumeSupported(supported);
}
/*package*/ boolean getBluetoothA2dpEnabled() {
- synchronized (mDeviceStateLock) {
+ synchronized (mDeviceBrokerLock) {
return mBluetoothA2dpEnabled;
}
}
/*package*/ int getA2dpCodec(@NonNull BluetoothDevice device) {
- synchronized (mDeviceStateLock) {
- return mBtHelper.getA2dpCodec(device);
- }
+ return mBtHelper.getA2dpCodec(device);
}
/*package*/ void dump(PrintWriter pw, String prefix) {
@@ -709,68 +749,50 @@ import java.io.PrintWriter;
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_RESTORE_DEVICES:
- synchronized (mDeviceStateLock) {
- mDeviceInventory.onRestoreDevices();
- mBtHelper.onAudioServerDiedRestoreA2dp();
- }
+ mDeviceInventory.onRestoreDevices();
+ mBtHelper.onAudioServerDiedRestoreA2dp();
break;
case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
- synchronized (mDeviceStateLock) {
- mDeviceInventory.onSetWiredDeviceConnectionState(
- (AudioDeviceInventory.WiredDeviceConnectionState) msg.obj);
- }
+ mDeviceInventory.onSetWiredDeviceConnectionState(
+ (AudioDeviceInventory.WiredDeviceConnectionState) msg.obj);
break;
case MSG_I_BROADCAST_BT_CONNECTION_STATE:
- synchronized (mDeviceStateLock) {
- mBtHelper.onBroadcastScoConnectionState(msg.arg1);
- }
+ mBtHelper.onBroadcastScoConnectionState(msg.arg1);
break;
case MSG_IIL_SET_FORCE_USE: // intended fall-through
case MSG_IIL_SET_FORCE_BT_A2DP_USE:
onSetForceUse(msg.arg1, msg.arg2, (String) msg.obj);
break;
case MSG_REPORT_NEW_ROUTES:
- synchronized (mDeviceStateLock) {
- mDeviceInventory.onReportNewRoutes();
- }
+ mDeviceInventory.onReportNewRoutes();
break;
case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED:
case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED:
- synchronized (mDeviceStateLock) {
- mDeviceInventory.onSetA2dpSinkConnectionState(
- (BtHelper.BluetoothA2dpDeviceInfo) msg.obj, msg.arg1);
- }
+ mDeviceInventory.onSetA2dpSinkConnectionState(
+ (BtHelper.BluetoothA2dpDeviceInfo) msg.obj, msg.arg1);
break;
case MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE:
- synchronized (mDeviceStateLock) {
- mDeviceInventory.onSetA2dpSourceConnectionState(
- (BtHelper.BluetoothA2dpDeviceInfo) msg.obj, msg.arg1);
- }
+ mDeviceInventory.onSetA2dpSourceConnectionState(
+ (BtHelper.BluetoothA2dpDeviceInfo) msg.obj, msg.arg1);
break;
case MSG_IL_SET_HEARING_AID_CONNECTION_STATE:
- synchronized (mDeviceStateLock) {
- mDeviceInventory.onSetHearingAidConnectionState(
- (BluetoothDevice) msg.obj, msg.arg1,
- mAudioService.getHearingAidStreamType());
- }
+ mDeviceInventory.onSetHearingAidConnectionState(
+ (BluetoothDevice) msg.obj, msg.arg1,
+ mAudioService.getHearingAidStreamType());
break;
case MSG_BT_HEADSET_CNCT_FAILED:
- synchronized (mSetModeLock) {
- synchronized (mDeviceStateLock) {
- mBtHelper.resetBluetoothSco();
- }
- }
+ mBtHelper.resetBluetoothSco();
break;
case MSG_IL_BTA2DP_DOCK_TIMEOUT:
// msg.obj == address of BTA2DP device
- synchronized (mDeviceStateLock) {
- mDeviceInventory.onMakeA2dpDeviceUnavailableNow((String) msg.obj, msg.arg1);
- }
+ mDeviceInventory.onMakeA2dpDeviceUnavailableNow((String) msg.obj, msg.arg1);
break;
case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
final int a2dpCodec;
final BluetoothDevice btDevice = (BluetoothDevice) msg.obj;
- synchronized (mDeviceStateLock) {
+ synchronized (mDeviceBrokerLock) {
+ // FIXME why isn't the codec coming with the request? lock shouldn't be
+ // needed here
a2dpCodec = mBtHelper.getA2dpCodec(btDevice);
mDeviceInventory.onBluetoothA2dpActiveDeviceChange(
new BtHelper.BluetoothA2dpDeviceInfo(btDevice, -1, a2dpCodec),
@@ -781,84 +803,48 @@ import java.io.PrintWriter;
onSendBecomingNoisyIntent();
break;
case MSG_II_SET_HEARING_AID_VOLUME:
- synchronized (mDeviceStateLock) {
- mBtHelper.setHearingAidVolume(msg.arg1, msg.arg2);
- }
+ mBtHelper.setHearingAidVolume(msg.arg1, msg.arg2);
break;
case MSG_I_SET_AVRCP_ABSOLUTE_VOLUME:
- synchronized (mDeviceStateLock) {
- mBtHelper.setAvrcpAbsoluteVolumeIndex(msg.arg1);
- }
+ mBtHelper.setAvrcpAbsoluteVolumeIndex(msg.arg1);
break;
case MSG_I_DISCONNECT_BT_SCO:
- synchronized (mSetModeLock) {
- synchronized (mDeviceStateLock) {
- mBtHelper.disconnectBluetoothSco(msg.arg1);
- }
- }
+ mBtHelper.disconnectBluetoothSco(msg.arg1);
break;
case MSG_L_SCOCLIENT_DIED:
- synchronized (mSetModeLock) {
- synchronized (mDeviceStateLock) {
- mBtHelper.scoClientDied(msg.obj);
- }
- }
+ mBtHelper.scoClientDied(msg.obj);
break;
case MSG_TOGGLE_HDMI:
- synchronized (mDeviceStateLock) {
- mDeviceInventory.onToggleHdmi();
- }
+ mDeviceInventory.onToggleHdmi();
break;
case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE:
- synchronized (mDeviceStateLock) {
- mDeviceInventory.onBluetoothA2dpActiveDeviceChange(
- (BtHelper.BluetoothA2dpDeviceInfo) msg.obj,
- BtHelper.EVENT_ACTIVE_DEVICE_CHANGE);
- }
+ mDeviceInventory.onBluetoothA2dpActiveDeviceChange(
+ (BtHelper.BluetoothA2dpDeviceInfo) msg.obj,
+ BtHelper.EVENT_ACTIVE_DEVICE_CHANGE);
break;
case MSG_DISCONNECT_A2DP:
- synchronized (mDeviceStateLock) {
- mDeviceInventory.disconnectA2dp();
- }
+ mDeviceInventory.disconnectA2dp();
break;
case MSG_DISCONNECT_A2DP_SINK:
- synchronized (mDeviceStateLock) {
- mDeviceInventory.disconnectA2dpSink();
- }
+ mDeviceInventory.disconnectA2dpSink();
break;
case MSG_DISCONNECT_BT_HEARING_AID:
- synchronized (mDeviceStateLock) {
- mDeviceInventory.disconnectHearingAid();
- }
+ mDeviceInventory.disconnectHearingAid();
break;
case MSG_DISCONNECT_BT_HEADSET:
- synchronized (mSetModeLock) {
- synchronized (mDeviceStateLock) {
- mBtHelper.disconnectHeadset();
- }
- }
+ mBtHelper.disconnectHeadset();
break;
case MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP:
- synchronized (mDeviceStateLock) {
- mBtHelper.onA2dpProfileConnected((BluetoothA2dp) msg.obj);
- }
+ mBtHelper.onA2dpProfileConnected((BluetoothA2dp) msg.obj);
break;
case MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP_SINK:
- synchronized (mDeviceStateLock) {
- mBtHelper.onA2dpSinkProfileConnected((BluetoothProfile) msg.obj);
- }
+ mBtHelper.onA2dpSinkProfileConnected((BluetoothProfile) msg.obj);
break;
case MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEARING_AID:
- synchronized (mDeviceStateLock) {
- mBtHelper.onHearingAidProfileConnected((BluetoothHearingAid) msg.obj);
- }
+ mBtHelper.onHearingAidProfileConnected((BluetoothHearingAid) msg.obj);
break;
case MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEADSET:
- synchronized (mSetModeLock) {
- synchronized (mDeviceStateLock) {
- mBtHelper.onHeadsetProfileConnected((BluetoothHeadset) msg.obj);
- }
- }
+ mBtHelper.onHeadsetProfileConnected((BluetoothHeadset) msg.obj);
break;
case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION:
case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION: {
@@ -871,11 +857,9 @@ import java.io.PrintWriter;
+ " addr=" + info.mDevice.getAddress()
+ " prof=" + info.mProfile + " supprNoisy=" + info.mSupprNoisy
+ " vol=" + info.mVolume)).printLog(TAG));
- synchronized (mDeviceStateLock) {
- mDeviceInventory.setBluetoothA2dpDeviceConnectionState(
- info.mDevice, info.mState, info.mProfile, info.mSupprNoisy,
- AudioSystem.DEVICE_NONE, info.mVolume);
- }
+ mDeviceInventory.setBluetoothA2dpDeviceConnectionState(
+ info.mDevice, info.mState, info.mProfile, info.mSupprNoisy,
+ AudioSystem.DEVICE_NONE, info.mVolume);
} break;
case MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT: {
final HearingAidDeviceConnectionInfo info =
@@ -885,10 +869,8 @@ import java.io.PrintWriter;
+ " addr=" + info.mDevice.getAddress()
+ " supprNoisy=" + info.mSupprNoisy
+ " src=" + info.mEventSource)).printLog(TAG));
- synchronized (mDeviceStateLock) {
- mDeviceInventory.setBluetoothHearingAidDeviceConnectionState(
- info.mDevice, info.mState, info.mSupprNoisy, info.mMusicDevice);
- }
+ mDeviceInventory.setBluetoothHearingAidDeviceConnectionState(
+ info.mDevice, info.mState, info.mSupprNoisy, info.mMusicDevice);
} break;
default:
Log.wtf(TAG, "Invalid message " + msg.what);
@@ -973,46 +955,57 @@ import java.io.PrintWriter;
/** If the msg is already queued, queue this one and leave the old. */
private static final int SENDMSG_QUEUE = 2;
+ @GuardedBy("mDeviceBrokerLock")
private void sendMsg(int msg, int existingMsgPolicy, int delay) {
sendIILMsg(msg, existingMsgPolicy, 0, 0, null, delay);
}
+ @GuardedBy("mDeviceBrokerLock")
private void sendILMsg(int msg, int existingMsgPolicy, int arg, Object obj, int delay) {
sendIILMsg(msg, existingMsgPolicy, arg, 0, obj, delay);
}
+ @GuardedBy("mDeviceBrokerLock")
private void sendLMsg(int msg, int existingMsgPolicy, Object obj, int delay) {
sendIILMsg(msg, existingMsgPolicy, 0, 0, obj, delay);
}
+ @GuardedBy("mDeviceBrokerLock")
private void sendIMsg(int msg, int existingMsgPolicy, int arg, int delay) {
sendIILMsg(msg, existingMsgPolicy, arg, 0, null, delay);
}
+ @GuardedBy("mDeviceBrokerLock")
private void sendMsgNoDelay(int msg, int existingMsgPolicy) {
sendIILMsg(msg, existingMsgPolicy, 0, 0, null, 0);
}
+ @GuardedBy("mDeviceBrokerLock")
private void sendIMsgNoDelay(int msg, int existingMsgPolicy, int arg) {
sendIILMsg(msg, existingMsgPolicy, arg, 0, null, 0);
}
+ @GuardedBy("mDeviceBrokerLock")
private void sendIIMsgNoDelay(int msg, int existingMsgPolicy, int arg1, int arg2) {
sendIILMsg(msg, existingMsgPolicy, arg1, arg2, null, 0);
}
+ @GuardedBy("mDeviceBrokerLock")
private void sendILMsgNoDelay(int msg, int existingMsgPolicy, int arg, Object obj) {
sendIILMsg(msg, existingMsgPolicy, arg, 0, obj, 0);
}
+ @GuardedBy("mDeviceBrokerLock")
private void sendLMsgNoDelay(int msg, int existingMsgPolicy, Object obj) {
sendIILMsg(msg, existingMsgPolicy, 0, 0, obj, 0);
}
+ @GuardedBy("mDeviceBrokerLock")
private void sendIILMsgNoDelay(int msg, int existingMsgPolicy, int arg1, int arg2, Object obj) {
sendIILMsg(msg, existingMsgPolicy, arg1, arg2, obj, 0);
}
+ @GuardedBy("mDeviceBrokerLock")
private void sendIILMsg(int msg, int existingMsgPolicy, int arg1, int arg2, Object obj,
int delay) {
if (existingMsgPolicy == SENDMSG_REPLACE) {
@@ -1031,31 +1024,29 @@ import java.io.PrintWriter;
Binder.restoreCallingIdentity(identity);
}
- synchronized (sLastDeviceConnectionMsgTimeLock) {
- long time = SystemClock.uptimeMillis() + delay;
+ long time = SystemClock.uptimeMillis() + delay;
- switch (msg) {
- case MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE:
- case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED:
- case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED:
- case MSG_IL_SET_HEARING_AID_CONNECTION_STATE:
- case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
- case MSG_IL_BTA2DP_DOCK_TIMEOUT:
- case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
- case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE:
- if (sLastDeviceConnectMsgTime >= time) {
- // add a little delay to make sure messages are ordered as expected
- time = sLastDeviceConnectMsgTime + 30;
- }
- sLastDeviceConnectMsgTime = time;
- break;
- default:
- break;
- }
-
- mBrokerHandler.sendMessageAtTime(mBrokerHandler.obtainMessage(msg, arg1, arg2, obj),
- time);
+ switch (msg) {
+ case MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE:
+ case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED:
+ case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED:
+ case MSG_IL_SET_HEARING_AID_CONNECTION_STATE:
+ case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
+ case MSG_IL_BTA2DP_DOCK_TIMEOUT:
+ case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
+ case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE:
+ if (sLastDeviceConnectMsgTime >= time) {
+ // add a little delay to make sure messages are ordered as expected
+ time = sLastDeviceConnectMsgTime + 30;
+ }
+ sLastDeviceConnectMsgTime = time;
+ break;
+ default:
+ break;
}
+
+ mBrokerHandler.sendMessageAtTime(mBrokerHandler.obtainMessage(msg, arg1, arg2, obj),
+ time);
}
//-------------------------------------------------------------
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 5bc2261878b6..075842b708ba 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -155,7 +155,7 @@ import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
/**
- * The implementation of the volume manager service.
+ * The implementation of the audio service for volume, audio focus, device management...
* <p>
* This implementation focuses on delivering a responsive UI. Most methods are
* asynchronous to external calls. For example, the task of setting a volume
@@ -469,12 +469,11 @@ public class AudioService extends IAudioService.Stub
// List of binder death handlers for setMode() client processes.
// The last process to have called setMode() is at the top of the list.
- // package-private so it can be accessed in AudioDeviceBroker.getSetModeDeathHandlers
- //TODO candidate to be moved to separate class that handles synchronization
- @GuardedBy("mDeviceBroker.mSetModeLock")
- /*package*/ final ArrayList<SetModeDeathHandler> mSetModeDeathHandlers =
+ private final ArrayList<SetModeDeathHandler> mSetModeDeathHandlers =
new ArrayList<SetModeDeathHandler>();
+ private volatile int mCurrentModeOwnerPid = 0;
+
// true if boot sequence has been completed
private boolean mSystemReady;
// true if Intent.ACTION_USER_SWITCHED has ever been received
@@ -845,7 +844,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 +860,6 @@ public class AudioService extends IAudioService.Stub
mFixedVolumeDevices &= ~AudioSystem.DEVICE_OUT_HDMI;
mFullVolumeDevices |= AudioSystem.DEVICE_OUT_HDMI;
}
- mHdmiCecSink = false;
mHdmiAudioSystemClient = mHdmiManager.getAudioSystemClient();
}
}
@@ -1113,8 +1116,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 +1126,7 @@ public class AudioService extends IAudioService.Stub
if (isPlatformTelevision()) {
synchronized (mHdmiClientLock) {
if (mHdmiManager != null) {
- mHdmiCecSink = false;
+ updateHdmiCecSinkLocked(mHdmiCecSink | false);
}
}
}
@@ -1902,16 +1904,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 +1917,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 +2358,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);
@@ -3139,15 +3149,10 @@ public class AudioService extends IAudioService.Stub
* @return 0 if nobody owns the mode
*/
/*package*/ int getModeOwnerPid() {
- int modeOwnerPid = 0;
- try {
- modeOwnerPid = mSetModeDeathHandlers.get(0).getPid();
- } catch (Exception e) {
- // nothing to do, modeOwnerPid is not modified
- }
- return modeOwnerPid;
+ return mCurrentModeOwnerPid;
}
+
private class SetModeDeathHandler implements IBinder.DeathRecipient {
private IBinder mCb; // To be notified of client's death
private int mPid;
@@ -3161,7 +3166,7 @@ public class AudioService extends IAudioService.Stub
public void binderDied() {
int oldModeOwnerPid = 0;
int newModeOwnerPid = 0;
- synchronized (mDeviceBroker.mSetModeLock) {
+ synchronized (mSetModeDeathHandlers) {
Log.w(TAG, "setMode() client died");
if (!mSetModeDeathHandlers.isEmpty()) {
oldModeOwnerPid = mSetModeDeathHandlers.get(0).getPid();
@@ -3172,11 +3177,15 @@ public class AudioService extends IAudioService.Stub
} else {
newModeOwnerPid = setModeInt(AudioSystem.MODE_NORMAL, mCb, mPid, TAG);
}
- }
- // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
- // SCO connections not started by the application changing the mode when pid changes
- if ((newModeOwnerPid != oldModeOwnerPid) && (newModeOwnerPid != 0)) {
- mDeviceBroker.postDisconnectBluetoothSco(newModeOwnerPid);
+
+ if (newModeOwnerPid != oldModeOwnerPid) {
+ mCurrentModeOwnerPid = newModeOwnerPid;
+ // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all SCO
+ // connections not started by the application changing the mode when pid changes
+ if (newModeOwnerPid != 0) {
+ mDeviceBroker.postDisconnectBluetoothSco(newModeOwnerPid);
+ }
+ }
}
}
@@ -3199,15 +3208,17 @@ public class AudioService extends IAudioService.Stub
/** @see AudioManager#setMode(int) */
public void setMode(int mode, IBinder cb, String callingPackage) {
- if (DEBUG_MODE) { Log.v(TAG, "setMode(mode=" + mode + ", callingPackage=" + callingPackage + ")"); }
+ if (DEBUG_MODE) {
+ Log.v(TAG, "setMode(mode=" + mode + ", callingPackage=" + callingPackage + ")");
+ }
if (!checkAudioSettingsPermission("setMode()")) {
return;
}
- if ( (mode == AudioSystem.MODE_IN_CALL) &&
- (mContext.checkCallingOrSelfPermission(
+ if ((mode == AudioSystem.MODE_IN_CALL)
+ && (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.MODIFY_PHONE_STATE)
- != PackageManager.PERMISSION_GRANTED)) {
+ != PackageManager.PERMISSION_GRANTED)) {
Log.w(TAG, "MODIFY_PHONE_STATE Permission Denial: setMode(MODE_IN_CALL) from pid="
+ Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
return;
@@ -3219,7 +3230,7 @@ public class AudioService extends IAudioService.Stub
int oldModeOwnerPid = 0;
int newModeOwnerPid = 0;
- synchronized (mDeviceBroker.mSetModeLock) {
+ synchronized (mSetModeDeathHandlers) {
if (!mSetModeDeathHandlers.isEmpty()) {
oldModeOwnerPid = mSetModeDeathHandlers.get(0).getPid();
}
@@ -3227,17 +3238,21 @@ public class AudioService extends IAudioService.Stub
mode = mMode;
}
newModeOwnerPid = setModeInt(mode, cb, Binder.getCallingPid(), callingPackage);
- }
- // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
- // SCO connections not started by the application changing the mode when pid changes
- if ((newModeOwnerPid != oldModeOwnerPid) && (newModeOwnerPid != 0)) {
- mDeviceBroker.postDisconnectBluetoothSco(newModeOwnerPid);
+
+ if (newModeOwnerPid != oldModeOwnerPid) {
+ mCurrentModeOwnerPid = newModeOwnerPid;
+ // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
+ // SCO connections not started by the application changing the mode when pid changes
+ if (newModeOwnerPid != 0) {
+ mDeviceBroker.postDisconnectBluetoothSco(newModeOwnerPid);
+ }
+ }
}
}
// setModeInt() returns a valid PID if the audio mode was successfully set to
// any mode other than NORMAL.
- @GuardedBy("mDeviceBroker.mSetModeLock")
+ @GuardedBy("mSetModeDeathHandlers")
private int setModeInt(int mode, IBinder cb, int pid, String caller) {
if (DEBUG_MODE) { Log.v(TAG, "setModeInt(mode=" + mode + ", pid=" + pid + ", caller="
+ caller + ")"); }
@@ -3576,9 +3591,7 @@ public class AudioService extends IAudioService.Stub
!mSystemReady) {
return;
}
- synchronized (mDeviceBroker.mSetModeLock) {
- mDeviceBroker.startBluetoothScoForClient_Sync(cb, scoAudioMode, eventSource);
- }
+ mDeviceBroker.startBluetoothScoForClient_Sync(cb, scoAudioMode, eventSource);
}
/** @see AudioManager#stopBluetoothSco() */
@@ -3590,9 +3603,7 @@ public class AudioService extends IAudioService.Stub
final String eventSource = new StringBuilder("stopBluetoothSco()")
.append(") from u/pid:").append(Binder.getCallingUid()).append("/")
.append(Binder.getCallingPid()).toString();
- synchronized (mDeviceBroker.mSetModeLock) {
- mDeviceBroker.stopBluetoothScoForClient_Sync(cb, eventSource);
- }
+ mDeviceBroker.stopBluetoothScoForClient_Sync(cb, eventSource);
}
@@ -4341,7 +4352,7 @@ public class AudioService extends IAudioService.Stub
// NOTE: Locking order for synchronized objects related to volume or ringer mode management:
// 1 mScoclient OR mSafeMediaVolumeState
- // 2 mSetModeLock
+ // 2 mSetModeDeathHandlers
// 3 mSettingsLock
// 4 VolumeStreamState.class
private class VolumeStreamState {
@@ -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/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 9f1a6bd15ac3..625b6b690443 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -171,8 +171,6 @@ public class BtHelper {
//----------------------------------------------------------------------
// Interface for AudioDeviceBroker
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void onSystemReady() {
mScoConnectionState = android.media.AudioManager.SCO_AUDIO_STATE_ERROR;
resetBluetoothSco();
@@ -245,8 +243,6 @@ public class BtHelper {
return mapBluetoothCodecToAudioFormat(btCodecConfig.getCodecType());
}
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void receiveBtEvent(Intent intent) {
final String action = intent.getAction();
if (action.equals(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) {
@@ -333,8 +329,6 @@ public class BtHelper {
*
* @param exceptPid pid whose SCO connections through {@link AudioManager} should be kept
*/
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void disconnectBluetoothSco(int exceptPid) {
checkScoAudioState();
if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL) {
@@ -343,8 +337,6 @@ public class BtHelper {
clearAllScoClients(exceptPid, true);
}
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void startBluetoothScoForClient(IBinder cb, int scoAudioMode,
@NonNull String eventSource) {
ScoClient client = getScoClient(cb, true);
@@ -364,8 +356,6 @@ public class BtHelper {
Binder.restoreCallingIdentity(ident);
}
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void stopBluetoothScoForClient(IBinder cb,
@NonNull String eventSource) {
ScoClient client = getScoClient(cb, false);
@@ -423,8 +413,6 @@ public class BtHelper {
mDeviceBroker.postDisconnectHearingAid();
}
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void resetBluetoothSco() {
clearAllScoClients(0, false);
mScoAudioState = SCO_STATE_INACTIVE;
@@ -433,8 +421,6 @@ public class BtHelper {
mDeviceBroker.setBluetoothScoOn(false, "resetBluetoothSco");
}
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void disconnectHeadset() {
setBtScoActiveDevice(null);
mBluetoothHeadset = null;
@@ -480,8 +466,6 @@ public class BtHelper {
/*eventSource*/ "mBluetoothProfileServiceListener");
}
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void onHeadsetProfileConnected(BluetoothHeadset headset) {
// Discard timeout message
mDeviceBroker.handleCancelFailureToConnectToBtHeadsetService();
@@ -568,8 +552,6 @@ public class BtHelper {
return result;
}
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
@GuardedBy("BtHelper.this")
private void setBtScoActiveDevice(BluetoothDevice btDevice) {
Log.i(TAG, "setBtScoActiveDevice: " + mBluetoothHeadsetDevice + " -> " + btDevice);
@@ -652,8 +634,6 @@ public class BtHelper {
};
//----------------------------------------------------------------------
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void scoClientDied(Object obj) {
final ScoClient client = (ScoClient) obj;
Log.w(TAG, "SCO client died");
@@ -684,8 +664,6 @@ public class BtHelper {
mDeviceBroker.postScoClientDied(this);
}
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- // @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
@GuardedBy("BtHelper.this")
void incCount(int scoAudioMode) {
if (!requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode)) {
@@ -705,8 +683,6 @@ public class BtHelper {
mStartcount++;
}
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- // @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
@GuardedBy("BtHelper.this")
void decCount() {
if (mStartcount == 0) {
@@ -726,8 +702,6 @@ public class BtHelper {
}
}
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- // @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
@GuardedBy("BtHelper.this")
void clearCount(boolean stopSco) {
if (mStartcount != 0) {
@@ -764,8 +738,6 @@ public class BtHelper {
return count;
}
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
@GuardedBy("BtHelper.this")
private boolean requestScoState(int state, int scoAudioMode) {
checkScoAudioState();
@@ -959,8 +931,6 @@ public class BtHelper {
return null;
}
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
@GuardedBy("BtHelper.this")
private void clearAllScoClients(int exceptPid, boolean stopSco) {
ScoClient savedClient = null;
diff --git a/services/core/java/com/android/server/audio/TEST_MAPPING b/services/core/java/com/android/server/audio/TEST_MAPPING
new file mode 100644
index 000000000000..0d34c5372914
--- /dev/null
+++ b/services/core/java/com/android/server/audio/TEST_MAPPING
@@ -0,0 +1,15 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsMediaTestCases",
+ "options": [
+ {
+ "include-filter": "android.media.cts.AudioManagerTest"
+ },
+ {
+ "include-filter": "android.media.cts.AudioFocusTest"
+ }
+ ]
+ }
+ ]
+}
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 4af3627427d2..7302b985181b 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -981,10 +981,10 @@ public class BiometricService extends SystemService {
mStatusBarService = mInjector.getStatusBarService();
// Cache the authenticators
- for (int i = 0; i < FEATURE_ID.length; i++) {
- if (hasFeature(FEATURE_ID[i])) {
+ for (int featureId : FEATURE_ID) {
+ if (hasFeature(featureId)) {
Authenticator authenticator =
- new Authenticator(FEATURE_ID[i], getAuthenticator(FEATURE_ID[i]));
+ new Authenticator(featureId, getAuthenticator(featureId));
mAuthenticators.add(authenticator);
}
}
@@ -1005,8 +1005,6 @@ public class BiometricService extends SystemService {
* and the error containing one of the {@link BiometricConstants} errors.
*/
private Pair<Integer, Integer> checkAndGetBiometricModality(int userId) {
- int modality = TYPE_NONE;
-
// No biometric features, send error
if (mAuthenticators.isEmpty()) {
return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT);
@@ -1022,10 +1020,11 @@ public class BiometricService extends SystemService {
boolean hasTemplatesEnrolled = false;
boolean enabledForApps = false;
+ int modality = TYPE_NONE;
int firstHwAvailable = TYPE_NONE;
- for (int i = 0; i < mAuthenticators.size(); i++) {
- modality = mAuthenticators.get(i).getType();
- BiometricAuthenticator authenticator = mAuthenticators.get(i).getAuthenticator();
+ for (Authenticator authenticatorWrapper : mAuthenticators) {
+ modality = authenticatorWrapper.getType();
+ BiometricAuthenticator authenticator = authenticatorWrapper.getAuthenticator();
if (authenticator.isHardwareDetected()) {
isHardwareDetected = true;
if (firstHwAvailable == TYPE_NONE) {
@@ -1036,9 +1035,6 @@ public class BiometricService extends SystemService {
if (authenticator.hasEnrolledTemplates(userId)) {
hasTemplatesEnrolled = true;
if (isEnabledForApp(modality, userId)) {
- // TODO(b/110907543): When face settings (and other settings) have both a
- // user toggle as well as a work profile settings page, this needs to be
- // updated to reflect the correct setting.
enabledForApps = true;
break;
}
@@ -1555,7 +1551,7 @@ public class BiometricService extends SystemService {
}
/**
- * authenticate() (above) which is called from BiometricPrompt determines which
+ * handleAuthenticate() (above) which is called from BiometricPrompt determines which
* modality/modalities to start authenticating with. authenticateInternal() should only be
* used for:
* 1) Preparing <Biometric>Services for authentication when BiometricPrompt#authenticate is,
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index 09f52860e069..96af74a60b1d 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -79,7 +79,7 @@ public class CameraServiceProxy extends SystemService
private static final int MSG_SWITCH_USER = 1;
private static final int RETRY_DELAY_TIME = 20; //ms
- private static final int RETRY_TIMES = 30;
+ private static final int RETRY_TIMES = 60;
// Maximum entries to keep in usage history before dumping out
private static final int MAX_USAGE_HISTORY = 100;
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index fc38735509f0..81e507cd24cf 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -19,7 +19,9 @@ package com.android.server.compat;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.util.Slog;
+import android.util.StatsLog;
+import com.android.internal.compat.ChangeReporter;
import com.android.internal.compat.IPlatformCompat;
import com.android.internal.util.DumpUtils;
@@ -34,23 +36,27 @@ public class PlatformCompat extends IPlatformCompat.Stub {
private static final String TAG = "Compatibility";
private final Context mContext;
+ private final ChangeReporter mChangeReporter;
public PlatformCompat(Context context) {
mContext = context;
+ mChangeReporter = new ChangeReporter();
}
@Override
public void reportChange(long changeId, ApplicationInfo appInfo) {
- Slog.d(TAG, "Compat change reported: " + changeId + "; UID " + appInfo.uid);
- // TODO log via StatsLog
+ reportChange(changeId, appInfo, StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__LOGGED);
}
@Override
public boolean isChangeEnabled(long changeId, ApplicationInfo appInfo) {
if (CompatConfig.get().isChangeEnabled(changeId, appInfo)) {
- reportChange(changeId, appInfo);
+ reportChange(changeId, appInfo,
+ StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__ENABLED);
return true;
}
+ reportChange(changeId, appInfo,
+ StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__DISABLED);
return false;
}
@@ -59,4 +65,13 @@ public class PlatformCompat extends IPlatformCompat.Stub {
if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, "platform_compat", pw)) return;
CompatConfig.get().dumpConfig(pw);
}
+
+ private void reportChange(long changeId, ApplicationInfo appInfo, int state) {
+ int uid = appInfo.uid;
+ //TODO(b/138374585): Implement rate limiting for the logs.
+ Slog.d(TAG, ChangeReporter.createLogString(uid, changeId, state));
+ mChangeReporter.reportChange(uid, changeId,
+ state, /* source */
+ StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__SOURCE__SYSTEM_SERVER);
+ }
}
diff --git a/services/core/java/com/android/server/display/color/ColorDisplayService.java b/services/core/java/com/android/server/display/color/ColorDisplayService.java
index 7fb5b191a9b0..0bf43b6d1b9c 100644
--- a/services/core/java/com/android/server/display/color/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/color/ColorDisplayService.java
@@ -609,6 +609,9 @@ public final class ColorDisplayService extends SystemService {
@Override
public void onAnimationEnd(Animator animator) {
+ Slog.d(TAG, tintController.getClass().getSimpleName()
+ + " Animation cancelled: " + mIsCancelled
+ + " to matrix: " + TintController.matrixToString(to, 16));
if (!mIsCancelled) {
// Ensure final color matrix is set at the end of the animation. If the
// animation is cancelled then don't set the final color matrix so the new
@@ -1314,8 +1317,10 @@ public final class ColorDisplayService extends SystemService {
* Reset the CCT value for the display white balance transform to its default value.
*/
public boolean resetDisplayWhiteBalanceColorTemperature() {
- return setDisplayWhiteBalanceColorTemperature(getContext().getResources()
- .getInteger(R.integer.config_displayWhiteBalanceColorTemperatureDefault));
+ int temperatureDefault = getContext().getResources()
+ .getInteger(R.integer.config_displayWhiteBalanceColorTemperatureDefault);
+ Slog.d(TAG, "resetDisplayWhiteBalanceColorTemperature: " + temperatureDefault);
+ return setDisplayWhiteBalanceColorTemperature(temperatureDefault);
}
/**
diff --git a/services/core/java/com/android/server/display/color/DisplayWhiteBalanceTintController.java b/services/core/java/com/android/server/display/color/DisplayWhiteBalanceTintController.java
index d2c6cd9f1007..3f1c222ab520 100644
--- a/services/core/java/com/android/server/display/color/DisplayWhiteBalanceTintController.java
+++ b/services/core/java/com/android/server/display/color/DisplayWhiteBalanceTintController.java
@@ -149,8 +149,6 @@ final class DisplayWhiteBalanceTintController extends TintController {
cct = mTemperatureMax;
}
- Slog.d(ColorDisplayService.TAG, "setDisplayWhiteBalanceTemperatureMatrix: cct = " + cct);
-
synchronized (mLock) {
mCurrentColorTemperature = cct;
@@ -185,6 +183,9 @@ final class DisplayWhiteBalanceTintController extends TintController {
java.lang.System.arraycopy(result, 3, mMatrixDisplayWhiteBalance, 4, 3);
java.lang.System.arraycopy(result, 6, mMatrixDisplayWhiteBalance, 8, 3);
}
+
+ Slog.d(ColorDisplayService.TAG, "setDisplayWhiteBalanceTemperatureMatrix: cct = " + cct
+ + " matrix = " + matrixToString(mMatrixDisplayWhiteBalance, 16));
}
@Override
@@ -225,28 +226,6 @@ final class DisplayWhiteBalanceTintController extends TintController {
}
}
- /**
- * Format a given matrix into a string.
- *
- * @param matrix the matrix to format
- * @param columns number of columns in the matrix
- */
- private String matrixToString(float[] matrix, int columns) {
- if (matrix == null || columns <= 0) {
- Slog.e(ColorDisplayService.TAG, "Invalid arguments when formatting matrix to string");
- return "";
- }
-
- final StringBuilder sb = new StringBuilder("");
- for (int i = 0; i < matrix.length; i++) {
- if (i % columns == 0) {
- sb.append("\n ");
- }
- sb.append(String.format("%9.6f", matrix[i]));
- }
- return sb.toString();
- }
-
private ColorSpace.Rgb makeRgbColorSpaceFromXYZ(float[] redGreenBlueXYZ, float[] whiteXYZ) {
return new ColorSpace.Rgb(
"Display Color Space",
diff --git a/services/core/java/com/android/server/display/color/TintController.java b/services/core/java/com/android/server/display/color/TintController.java
index b291c645027a..8d8b9b2af04e 100644
--- a/services/core/java/com/android/server/display/color/TintController.java
+++ b/services/core/java/com/android/server/display/color/TintController.java
@@ -18,6 +18,7 @@ package com.android.server.display.color;
import android.animation.ValueAnimator;
import android.content.Context;
+import android.util.Slog;
import java.io.PrintWriter;
@@ -95,4 +96,29 @@ abstract class TintController {
* Returns whether or not this transform type is available on this device.
*/
public abstract boolean isAvailable(Context context);
+
+ /**
+ * Format a given matrix into a string.
+ *
+ * @param matrix the matrix to format
+ * @param columns number of columns in the matrix
+ */
+ static String matrixToString(float[] matrix, int columns) {
+ if (matrix == null || columns <= 0) {
+ Slog.e(ColorDisplayService.TAG, "Invalid arguments when formatting matrix to string,"
+ + " matrix is null: " + (matrix == null)
+ + " columns: " + columns);
+ return "";
+ }
+
+ final StringBuilder sb = new StringBuilder("");
+ for (int i = 0; i < matrix.length; i++) {
+ if (i % columns == 0) {
+ sb.append("\n ");
+ }
+ sb.append(String.format("%9.6f", matrix[i]));
+ }
+ return sb.toString();
+ }
+
}
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
index 02ec10e2d49d..7b1f4c3222f3 100644
--- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
@@ -95,6 +95,11 @@ public class DisplayWhiteBalanceController implements
// A piecewise linear relationship between high light brightness and high light bias.
private Spline.LinearSpline mHighLightAmbientBrightnessToBiasSpline;
+ private float mLatestAmbientColorTemperature;
+ private float mLatestAmbientBrightness;
+ private float mLatestLowLightBias;
+ private float mLatestHighLightBias;
+
/**
* @param brightnessSensor
* The sensor used to detect changes in the ambient brightness.
@@ -348,6 +353,7 @@ public class DisplayWhiteBalanceController implements
public void updateAmbientColorTemperature() {
final long time = System.currentTimeMillis();
float ambientColorTemperature = mColorTemperatureFilter.getEstimate(time);
+ mLatestAmbientColorTemperature = ambientColorTemperature;
if (mAmbientToDisplayColorTemperatureSpline != null && ambientColorTemperature != -1.0f) {
ambientColorTemperature =
@@ -355,6 +361,7 @@ public class DisplayWhiteBalanceController implements
}
float ambientBrightness = mBrightnessFilter.getEstimate(time);
+ mLatestAmbientBrightness = ambientBrightness;
if (ambientColorTemperature != -1.0f &&
mLowLightAmbientBrightnessToBiasSpline != null) {
@@ -362,6 +369,7 @@ public class DisplayWhiteBalanceController implements
ambientColorTemperature =
bias * ambientColorTemperature + (1.0f - bias)
* mLowLightAmbientColorTemperature;
+ mLatestLowLightBias = bias;
}
if (ambientColorTemperature != -1.0f &&
mHighLightAmbientBrightnessToBiasSpline != null) {
@@ -369,6 +377,7 @@ public class DisplayWhiteBalanceController implements
ambientColorTemperature =
(1.0f - bias) * ambientColorTemperature + bias
* mHighLightAmbientColorTemperature;
+ mLatestHighLightBias = bias;
}
if (mAmbientColorTemperatureOverride != -1.0f) {
@@ -426,6 +435,11 @@ public class DisplayWhiteBalanceController implements
}
mPendingAmbientColorTemperature = -1.0f;
mAmbientColorTemperatureHistory.add(mAmbientColorTemperature);
+ Slog.d(TAG, "Display cct: " + mAmbientColorTemperature
+ + " Latest ambient cct: " + mLatestAmbientColorTemperature
+ + " Latest ambient lux: " + mLatestAmbientBrightness
+ + " Latest low light bias: " + mLatestLowLightBias
+ + " Latest high light bias: " + mLatestHighLightBias);
mColorDisplayServiceInternal.setDisplayWhiteBalanceColorTemperature(
(int) mAmbientColorTemperature);
mLastAmbientColorTemperature = mAmbientColorTemperature;
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/integrity/OWNERS b/services/core/java/com/android/server/integrity/OWNERS
new file mode 100644
index 000000000000..019aa4fb0f2b
--- /dev/null
+++ b/services/core/java/com/android/server/integrity/OWNERS
@@ -0,0 +1,6 @@
+omernebil@google.com
+khelmy@google.com
+mdchurchill@google.com
+sturla@google.com
+songpan@google.com
+bjy@google.com
diff --git a/services/core/java/com/android/server/location/AbstractLocationProvider.java b/services/core/java/com/android/server/location/AbstractLocationProvider.java
index c1a63940c080..ccfc98e2291b 100644
--- a/services/core/java/com/android/server/location/AbstractLocationProvider.java
+++ b/services/core/java/com/android/server/location/AbstractLocationProvider.java
@@ -18,7 +18,7 @@ package com.android.server.location;
import android.content.Context;
import android.location.Location;
-import android.location.LocationProvider;
+import android.os.Binder;
import android.os.Bundle;
import android.os.WorkSource;
@@ -81,7 +81,12 @@ public abstract class AbstractLocationProvider {
* any thread.
*/
protected void setEnabled(boolean enabled) {
- mLocationProviderManager.onSetEnabled(enabled);
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mLocationProviderManager.onSetEnabled(enabled);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
/**
@@ -89,21 +94,36 @@ public abstract class AbstractLocationProvider {
* any thread.
*/
protected void setProperties(ProviderProperties properties) {
- mLocationProviderManager.onSetProperties(properties);
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mLocationProviderManager.onSetProperties(properties);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
/**
* Call this method to report a new location. May be called from any thread.
*/
protected void reportLocation(Location location) {
- mLocationProviderManager.onReportLocation(location);
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mLocationProviderManager.onReportLocation(location);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
/**
* Call this method to report a new location. May be called from any thread.
*/
protected void reportLocation(List<Location> locations) {
- mLocationProviderManager.onReportLocation(locations);
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mLocationProviderManager.onReportLocation(locations);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
/**
@@ -132,26 +152,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/NotificationComparator.java b/services/core/java/com/android/server/notification/NotificationComparator.java
index 9b9f4de7a18f..bc051547a53f 100644
--- a/services/core/java/com/android/server/notification/NotificationComparator.java
+++ b/services/core/java/com/android/server/notification/NotificationComparator.java
@@ -129,6 +129,12 @@ public class NotificationComparator
return -1 * Integer.compare(leftPriority, rightPriority);
}
+ final boolean leftInterruptive = left.isInterruptive();
+ final boolean rightInterruptive = right.isInterruptive();
+ if (leftInterruptive != rightInterruptive) {
+ return -1 * Boolean.compare(leftInterruptive, rightInterruptive);
+ }
+
// then break ties by time, most recent first
return -1 * Long.compare(left.getRankingTimeMs(), right.getRankingTimeMs());
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 2d4c6cf70847..d480cb6e9800 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -5759,7 +5759,9 @@ public class NotificationManagerService extends SystemService {
notification.flags |=
old.getNotification().flags & FLAG_FOREGROUND_SERVICE;
r.isUpdate = true;
- r.setTextChanged(isVisuallyInterruptive(old, r));
+ final boolean isInterruptive = isVisuallyInterruptive(old, r);
+ r.setTextChanged(isInterruptive);
+ r.setInterruptive(isInterruptive);
}
mNotificationsByKey.put(n.getKey(), r);
@@ -5858,7 +5860,6 @@ public class NotificationManagerService extends SystemService {
Notification oldN = old.sbn.getNotification();
Notification newN = r.sbn.getNotification();
-
if (oldN.extras == null || newN.extras == null) {
if (DEBUG_INTERRUPTIVENESS) {
Slog.v(TAG, "INTERRUPTIVENESS: "
@@ -5890,6 +5891,7 @@ public class NotificationManagerService extends SystemService {
}
return true;
}
+
// Do not compare Spannables (will always return false); compare unstyled Strings
final String oldText = String.valueOf(oldN.extras.get(Notification.EXTRA_TEXT));
final String newText = String.valueOf(newN.extras.get(Notification.EXTRA_TEXT));
@@ -5904,6 +5906,7 @@ public class NotificationManagerService extends SystemService {
}
return true;
}
+
if (oldN.hasCompletedProgress() != newN.hasCompletedProgress()) {
if (DEBUG_INTERRUPTIVENESS) {
Slog.v(TAG, "INTERRUPTIVENESS: "
@@ -5911,6 +5914,16 @@ public class NotificationManagerService extends SystemService {
}
return true;
}
+
+ // Fields below are invisible to bubbles.
+ if (r.canBubble()) {
+ if (DEBUG_INTERRUPTIVENESS) {
+ Slog.v(TAG, "INTERRUPTIVENESS: "
+ + r.getKey() + " is not interruptive: bubble");
+ }
+ return false;
+ }
+
// Actions
if (Notification.areActionsVisiblyDifferent(oldN, newN)) {
if (DEBUG_INTERRUPTIVENESS) {
@@ -5944,7 +5957,6 @@ public class NotificationManagerService extends SystemService {
} catch (Exception e) {
Slog.w(TAG, "error recovering builder", e);
}
-
return false;
}
@@ -6139,12 +6151,17 @@ public class NotificationManagerService extends SystemService {
Slog.v(TAG, "INTERRUPTIVENESS: "
+ record.getKey() + " is not interruptive: summary");
}
+ } else if (record.canBubble()) {
+ if (DEBUG_INTERRUPTIVENESS) {
+ Slog.v(TAG, "INTERRUPTIVENESS: "
+ + record.getKey() + " is not interruptive: bubble");
+ }
} else {
+ record.setInterruptive(true);
if (DEBUG_INTERRUPTIVENESS) {
Slog.v(TAG, "INTERRUPTIVENESS: "
+ record.getKey() + " is interruptive: alerted");
}
- record.setInterruptive(true);
}
MetricsLogger.action(record.getLogMaker()
.setCategory(MetricsEvent.NOTIFICATION_ALERT)
@@ -6503,15 +6520,21 @@ public class NotificationManagerService extends SystemService {
int indexBefore = findNotificationRecordIndexLocked(record);
boolean interceptBefore = record.isIntercepted();
int visibilityBefore = record.getPackageVisibilityOverride();
+ boolean interruptiveBefore = record.isInterruptive();
+
recon.applyChangesLocked(record);
applyZenModeLocked(record);
mRankingHelper.sort(mNotificationList);
- int indexAfter = findNotificationRecordIndexLocked(record);
- boolean interceptAfter = record.isIntercepted();
- int visibilityAfter = record.getPackageVisibilityOverride();
- changed = indexBefore != indexAfter || interceptBefore != interceptAfter
- || visibilityBefore != visibilityAfter;
- if (interceptBefore && !interceptAfter
+ boolean indexChanged = indexBefore != findNotificationRecordIndexLocked(record);
+ boolean interceptChanged = interceptBefore != record.isIntercepted();
+ boolean visibilityChanged = visibilityBefore != record.getPackageVisibilityOverride();
+
+ // Broadcast isInterruptive changes for bubbles.
+ boolean interruptiveChanged =
+ record.canBubble() && (interruptiveBefore != record.isInterruptive());
+
+ changed = indexChanged || interceptChanged || visibilityChanged || interruptiveChanged;
+ if (interceptBefore && !record.isIntercepted()
&& record.isNewEnoughForAlerting(System.currentTimeMillis())) {
buzzBeepBlinkLocked(record);
}
@@ -7661,7 +7684,8 @@ public class NotificationManagerService extends SystemService {
record.getSound() != null || record.getVibration() != null,
record.getSystemGeneratedSmartActions(),
record.getSmartReplies(),
- record.canBubble()
+ record.canBubble(),
+ record.isInterruptive()
);
rankings.add(ranking);
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 965ddc9f2782..f8b3fb259089 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -744,7 +744,7 @@ public final class OverlayManagerService extends SystemService {
@NonNull final FileDescriptor out, @NonNull final FileDescriptor err,
@NonNull final String[] args, @NonNull final ShellCallback callback,
@NonNull final ResultReceiver resultReceiver) {
- (new OverlayManagerShellCommand(this)).exec(
+ (new OverlayManagerShellCommand(getContext(), this)).exec(
this, in, out, err, args, callback, resultReceiver);
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
index 99f583978535..eb432759cb02 100644
--- a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
+++ b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
@@ -18,15 +18,23 @@ package com.android.server.om;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.Context;
import android.content.om.IOverlayManager;
import android.content.om.OverlayInfo;
+import android.content.pm.PackageManager;
+import android.content.res.AssetManager;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.os.RemoteException;
import android.os.ShellCommand;
import android.os.UserHandle;
+import android.util.TypedValue;
import java.io.PrintWriter;
import java.util.List;
import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* Implementation of 'cmd overlay' commands.
@@ -36,9 +44,11 @@ import java.util.Map;
* for a list of available commands.
*/
final class OverlayManagerShellCommand extends ShellCommand {
+ private final Context mContext;
private final IOverlayManager mInterface;
- OverlayManagerShellCommand(@NonNull final IOverlayManager iom) {
+ OverlayManagerShellCommand(@NonNull final Context ctx, @NonNull final IOverlayManager iom) {
+ mContext = ctx;
mInterface = iom;
}
@@ -60,6 +70,8 @@ final class OverlayManagerShellCommand extends ShellCommand {
return runEnableExclusive();
case "set-priority":
return runSetPriority();
+ case "lookup":
+ return runLookup();
default:
return handleDefaultCommands(cmd);
}
@@ -102,6 +114,10 @@ final class OverlayManagerShellCommand extends ShellCommand {
out.println(" 'lowest', change priority of PACKAGE to the lowest priority.");
out.println(" If PARENT is the special keyword 'highest', change priority of");
out.println(" PACKAGE to the highest priority.");
+ out.println(" lookup [--verbose] PACKAGE-TO-LOAD PACKAGE:TYPE/NAME");
+ out.println(" Load a package and print the value of a given resource");
+ out.println(" applying the current configuration and enabled overlays.");
+ out.println(" For a more fine-grained alernative, use 'idmap2 lookup'.");
}
private int runList() throws RemoteException {
@@ -253,4 +269,92 @@ final class OverlayManagerShellCommand extends ShellCommand {
return mInterface.setPriority(packageName, newParentPackageName, userId) ? 0 : 1;
}
}
+
+ private int runLookup() throws RemoteException {
+ final PrintWriter out = getOutPrintWriter();
+ final PrintWriter err = getErrPrintWriter();
+
+ final boolean verbose = "--verbose".equals(getNextOption());
+
+ final String packageToLoad = getNextArgRequired();
+
+ final String fullyQualifiedResourceName = getNextArgRequired(); // package:type/name
+ final Pattern regex = Pattern.compile("(.*?):(.*?)/(.*?)");
+ final Matcher matcher = regex.matcher(fullyQualifiedResourceName);
+ if (!matcher.matches()) {
+ err.println("Error: bad resource name, doesn't match package:type/name");
+ return 1;
+ }
+
+ final PackageManager pm = mContext.getPackageManager();
+ if (pm == null) {
+ err.println("Error: failed to get package manager");
+ return 1;
+ }
+
+ final Resources res;
+ try {
+ res = pm.getResourcesForApplication(packageToLoad);
+ } catch (PackageManager.NameNotFoundException e) {
+ err.println("Error: failed to get resources for package " + packageToLoad);
+ return 1;
+ }
+ final AssetManager assets = res.getAssets();
+ try {
+ assets.setResourceResolutionLoggingEnabled(true);
+
+ // first try as non-complex type ...
+ try {
+ final TypedValue value = new TypedValue();
+ res.getValue(fullyQualifiedResourceName, value, false /* resolveRefs */);
+ final CharSequence valueString = value.coerceToString();
+ final String resolution = assets.getLastResourceResolution();
+
+ res.getValue(fullyQualifiedResourceName, value, true /* resolveRefs */);
+ final CharSequence resolvedString = value.coerceToString();
+
+ if (verbose) {
+ out.println(resolution);
+ }
+
+ if (valueString.equals(resolvedString)) {
+ out.println(valueString);
+ } else {
+ out.println(valueString + " -> " + resolvedString);
+ }
+ return 0;
+ } catch (Resources.NotFoundException e) {
+ // this is ok, resource could still be a complex type
+ }
+
+ // ... then try as complex type
+ try {
+
+ final String pkg = matcher.group(1);
+ final String type = matcher.group(2);
+ final String name = matcher.group(3);
+ final int resid = res.getIdentifier(name, type, pkg);
+ if (resid == 0) {
+ throw new Resources.NotFoundException();
+ }
+ final TypedArray array = res.obtainTypedArray(resid);
+ if (verbose) {
+ out.println(assets.getLastResourceResolution());
+ }
+ TypedValue tv = new TypedValue();
+ for (int i = 0; i < array.length(); i++) {
+ array.getValue(i, tv);
+ out.println(tv.coerceToString());
+ }
+ array.recycle();
+ return 0;
+ } catch (Resources.NotFoundException e) {
+ // give up
+ err.println("Error: failed to get the resource " + fullyQualifiedResourceName);
+ return 1;
+ }
+ } finally {
+ assets.setResourceResolutionLoggingEnabled(false);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 7eb74381f7ae..61ea84f9dc7f 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;
@@ -34,10 +39,12 @@ import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
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;
@@ -49,19 +56,16 @@ import java.util.Set;
* The entity responsible for filtering visibility between apps based on declarations in their
* manifests.
*/
-class AppsFilter {
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public class AppsFilter {
private static final String TAG = PackageManagerService.TAG;
- // 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;
-
// 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
@@ -122,15 +126,13 @@ class AppsFilter {
/** @return true if the feature is enabled for the given package. */
boolean packageIsEnabled(PackageParser.Package pkg);
+
}
private static class FeatureConfigImpl implements FeatureConfig {
private static final String FILTERING_ENABLED_NAME = "package_query_filtering_enabled";
-
- // STOPSHIP(patb): set this to true if we plan to launch this in R
- private static final boolean DEFAULT_ENABLED_STATE = false;
private final PackageManagerService.Injector mInjector;
- private volatile boolean mFeatureEnabled = DEFAULT_ENABLED_STATE;
+ private volatile boolean mFeatureEnabled = true;
private FeatureConfigImpl(PackageManagerService.Injector injector) {
mInjector = injector;
@@ -140,13 +142,13 @@ class AppsFilter {
public void onSystemReady() {
mFeatureEnabled = DeviceConfig.getBoolean(
NAMESPACE_PACKAGE_MANAGER_SERVICE, FILTERING_ENABLED_NAME,
- DEFAULT_ENABLED_STATE);
+ true);
DeviceConfig.addOnPropertiesChangedListener(
NAMESPACE_PACKAGE_MANAGER_SERVICE, FgThread.getExecutor(),
properties -> {
synchronized (FeatureConfigImpl.this) {
mFeatureEnabled = properties.getBoolean(
- FILTERING_ENABLED_NAME, DEFAULT_ENABLED_STATE);
+ FILTERING_ENABLED_NAME, true);
}
});
}
@@ -200,45 +202,65 @@ 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;
}
/**
- * Marks that a package initiated an interaction with another package, granting visibility of
- * the prior from the former.
+ * Grants access based on an interaction between a calling and target package, granting
+ * visibility of the caller from the target.
*
- * @param initiatingPackage the package initiating the interaction
+ * @param callingPackage the package initiating the interaction
* @param targetPackage the package being interacted with and thus gaining visibility of the
* initiating package.
* @param userId the user in which this interaction was taking place
*/
- private void markAppInteraction(
- PackageSetting initiatingPackage, PackageSetting targetPackage, int userId) {
+ public void grantImplicitAccess(
+ String callingPackage, String targetPackage, int userId) {
HashMap<String, Set<String>> currentUser = mImplicitlyQueryable.get(userId);
if (currentUser == null) {
currentUser = new HashMap<>();
mImplicitlyQueryable.put(userId, currentUser);
}
- if (!currentUser.containsKey(targetPackage.pkg.packageName)) {
- currentUser.put(targetPackage.pkg.packageName, new HashSet<>());
+ if (!currentUser.containsKey(targetPackage)) {
+ currentUser.put(targetPackage, new HashSet<>());
}
- currentUser.get(targetPackage.pkg.packageName).add(initiatingPackage.pkg.packageName);
+ currentUser.get(targetPackage).add(callingPackage);
}
public void onSystemReady() {
@@ -327,10 +349,16 @@ class AppsFilter {
public boolean shouldFilterApplication(int callingUid, @Nullable SettingBase callingSetting,
PackageSetting targetPkgSetting, int userId) {
final boolean featureEnabled = mFeatureConfig.isGloballyEnabled();
- if (!featureEnabled && !DEBUG_RUN_WHEN_DISABLED) {
+ if (!featureEnabled) {
+ 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) {
@@ -342,8 +370,6 @@ class AppsFilter {
callingPkgSetting = (PackageSetting) callingSetting;
if (!shouldFilterApplicationInternal(callingPkgSetting, targetPkgSetting,
userId)) {
- // TODO: actually base this on a start / launch (not just a query)
- markAppInteraction(callingPkgSetting, targetPkgSetting, userId);
return false;
}
} else if (callingSetting instanceof SharedUserSetting) {
@@ -354,8 +380,6 @@ class AppsFilter {
final PackageSetting packageSetting = packageSettings.valueAt(i);
if (!shouldFilterApplicationInternal(packageSetting, targetPkgSetting,
userId)) {
- // TODO: actually base this on a start / launch (not just a query)
- markAppInteraction(packageSetting, targetPkgSetting, userId);
return false;
}
if (callingPkgSetting == null && packageSetting.pkg != null) {
@@ -371,22 +395,12 @@ class AppsFilter {
return true;
}
}
- if (!featureEnabled) {
- return false;
- }
- if (mFeatureConfig.packageIsEnabled(callingPkgSetting.pkg)) {
- if (DEBUG_LOGGING) {
- Slog.d(TAG, "interaction: " + callingPkgSetting.name + " -> "
- + targetPkgSetting.name + (DEBUG_ALLOW_ALL ? " ALLOWED" : "BLOCKED"));
- }
- return !DEBUG_ALLOW_ALL;
- } else {
- if (DEBUG_LOGGING) {
- Slog.d(TAG, "interaction: " + callingPkgSetting.name + " -> "
- + targetPkgSetting.name + " DISABLED");
- }
- return false;
+
+ if (DEBUG_LOGGING) {
+ log(callingPkgSetting, targetPkgSetting,
+ DEBUG_ALLOW_ALL ? "ALLOWED" : "BLOCKED");
}
+ return !DEBUG_ALLOW_ALL;
}
private boolean shouldFilterApplicationInternal(
@@ -394,41 +408,74 @@ class AppsFilter {
final String callingName = callingPkgSetting.pkg.packageName;
final PackageParser.Package targetPkg = targetPkgSetting.pkg;
+ if (!mFeatureConfig.packageIsEnabled(callingPkgSetting.pkg)) {
+ if (DEBUG_LOGGING) {
+ log(callingPkgSetting, targetPkgSetting, "DISABLED");
+ }
+ return false;
+ }
// 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 +484,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 +495,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 +524,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/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java
index 5eaddf9ffa05..9e04c4b69bd0 100644
--- a/services/core/java/com/android/server/pm/InstantAppRegistry.java
+++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java
@@ -401,7 +401,7 @@ class InstantAppRegistry {
@GuardedBy("mService.mLock")
public void grantInstantAccessLPw(@UserIdInt int userId, @Nullable Intent intent,
- int targetAppId, int instantAppId) {
+ int instantAppId, int targetAppId) {
if (mInstalledInstantAppUids == null) {
return; // no instant apps installed; no need to grant
}
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/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index b632d18f5cd2..8b242242cbe3 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -491,6 +491,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
params.installFlags &= ~PackageManager.INSTALL_FROM_ADB;
params.installFlags &= ~PackageManager.INSTALL_ALL_USERS;
+ params.installFlags &= ~PackageManager.INSTALL_ALLOW_TEST;
params.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
if ((params.installFlags & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0
&& !mPm.isCallerVerifier(callingUid)) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b124c4b18efa..d03caef1ce95 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -581,16 +581,6 @@ public class PackageManagerService extends IPackageManager.Stub
private static final String PACKAGE_SCHEME = "package";
- private static final String VENDOR_OVERLAY_DIR = "/vendor/overlay";
-
- private static final String PRODUCT_OVERLAY_DIR = "/product/overlay";
-
- private static final String SYSTEM_EXT_OVERLAY_DIR = "/system_ext/overlay";
-
- private static final String ODM_OVERLAY_DIR = "/odm/overlay";
-
- private static final String OEM_OVERLAY_DIR = "/oem/overlay";
-
/** Canonical intent used to identify what counts as a "web browser" app */
private static final Intent sBrowserIntent;
static {
@@ -756,6 +746,26 @@ public class PackageManagerService extends IPackageManager.Stub
private final Injector mInjector;
/**
+ * The list of all system partitions that may contain packages in ascending order of
+ * specificity (the more generic, the earlier in the list a partition appears).
+ */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static final List<SystemPartition> SYSTEM_PARTITIONS = Collections.unmodifiableList(
+ Arrays.asList(
+ new SystemPartition(Environment.getRootDirectory(), 0 /* scanFlag */,
+ true /* hasPriv */, false /* hasOverlays */),
+ new SystemPartition(Environment.getVendorDirectory(), SCAN_AS_VENDOR,
+ true /* hasPriv */, true /* hasOverlays */),
+ new SystemPartition(Environment.getOdmDirectory(), SCAN_AS_ODM,
+ true /* hasPriv */, true /* hasOverlays */),
+ new SystemPartition(Environment.getOemDirectory(), SCAN_AS_OEM,
+ false /* hasPriv */, true /* hasOverlays */),
+ new SystemPartition(Environment.getProductDirectory(), SCAN_AS_PRODUCT,
+ true /* hasPriv */, true /* hasOverlays */),
+ new SystemPartition(Environment.getSystemExtDirectory(), SCAN_AS_SYSTEM_EXT,
+ true /* hasPriv */, true /* hasOverlays */)));
+
+ /**
* Unit tests will instantiate, extend and/or mock to mock dependencies / behaviors.
*
* NOTE: All getters should return the same instance for every call.
@@ -2552,6 +2562,51 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static class SystemPartition {
+ public final File folder;
+ public final int scanFlag;
+ public final File appFolder;
+ @Nullable
+ public final File privAppFolder;
+ @Nullable
+ public final File overlayFolder;
+
+ private SystemPartition(File folder, int scanFlag, boolean hasPrivApps,
+ boolean hasOverlays) {
+ this.folder = folder;
+ this.scanFlag = scanFlag;
+ this.appFolder = toCanonical(new File(folder, "app"));
+ this.privAppFolder = hasPrivApps ? toCanonical(new File(folder, "priv-app")) : null;
+ this.overlayFolder = hasOverlays ? toCanonical(new File(folder, "overlay")) : null;
+ }
+
+ public boolean containsPrivApp(File scanFile) {
+ return FileUtils.contains(privAppFolder, scanFile);
+ }
+
+ public boolean containsApp(File scanFile) {
+ return FileUtils.contains(appFolder, scanFile);
+ }
+
+ public boolean containsPath(String path) {
+ return path.startsWith(folder.getPath() + "/");
+ }
+
+ public boolean containsPrivPath(String path) {
+ return privAppFolder != null && path.startsWith(privAppFolder.getPath() + "/");
+ }
+
+ private static File toCanonical(File dir) {
+ try {
+ return dir.getCanonicalFile();
+ } catch (IOException e) {
+ // failed to look up canonical path, continue with original one
+ return dir;
+ }
+ }
+ }
+
public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest) {
final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing",
Trace.TRACE_TAG_PACKAGE_MANAGER);
@@ -2775,215 +2830,35 @@ public class PackageManagerService extends IPackageManager.Stub
// any apps.)
// For security and version matching reason, only consider overlay packages if they
// reside in the right directory.
- scanDirTracedLI(new File(VENDOR_OVERLAY_DIR),
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_VENDOR,
- 0);
- scanDirTracedLI(new File(PRODUCT_OVERLAY_DIR),
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_PRODUCT,
- 0);
- scanDirTracedLI(new File(SYSTEM_EXT_OVERLAY_DIR),
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_SYSTEM_EXT,
- 0);
- scanDirTracedLI(new File(ODM_OVERLAY_DIR),
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_ODM,
- 0);
- scanDirTracedLI(new File(OEM_OVERLAY_DIR),
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_OEM,
- 0);
+ final int systemParseFlags = mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR;
+ final int systemScanFlags = scanFlags | SCAN_AS_SYSTEM;
+ for (int i = SYSTEM_PARTITIONS.size() - 1; i >= 0; i--) {
+ final SystemPartition partition = SYSTEM_PARTITIONS.get(i);
+ if (partition.overlayFolder == null) {
+ continue;
+ }
+ scanDirTracedLI(partition.overlayFolder, systemParseFlags,
+ systemScanFlags | partition.scanFlag, 0);
+ }
mParallelPackageParserCallback.findStaticOverlayPackages();
- // Find base frameworks (resource packages without code).
- scanDirTracedLI(frameworkDir,
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_NO_DEX
- | SCAN_AS_SYSTEM
- | SCAN_AS_PRIVILEGED,
- 0);
+ scanDirTracedLI(frameworkDir, systemParseFlags,
+ systemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, 0);
if (!mPackages.containsKey("android")) {
throw new IllegalStateException(
"Failed to load frameworks package; check log for warnings");
}
-
- // Collect privileged system packages.
- final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
- scanDirTracedLI(privilegedAppDir,
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_PRIVILEGED,
- 0);
-
- // Collect ordinary system packages.
- final File systemAppDir = new File(Environment.getRootDirectory(), "app");
- scanDirTracedLI(systemAppDir,
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM,
- 0);
-
- // Collect privileged vendor packages.
- File privilegedVendorAppDir = new File(Environment.getVendorDirectory(), "priv-app");
- try {
- privilegedVendorAppDir = privilegedVendorAppDir.getCanonicalFile();
- } catch (IOException e) {
- // failed to look up canonical path, continue with original one
- }
- scanDirTracedLI(privilegedVendorAppDir,
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_VENDOR
- | SCAN_AS_PRIVILEGED,
- 0);
-
- // Collect ordinary vendor packages.
- File vendorAppDir = new File(Environment.getVendorDirectory(), "app");
- try {
- vendorAppDir = vendorAppDir.getCanonicalFile();
- } catch (IOException e) {
- // failed to look up canonical path, continue with original one
- }
- scanDirTracedLI(vendorAppDir,
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_VENDOR,
- 0);
-
- // Collect privileged odm packages. /odm is another vendor partition
- // other than /vendor.
- File privilegedOdmAppDir = new File(Environment.getOdmDirectory(),
- "priv-app");
- try {
- privilegedOdmAppDir = privilegedOdmAppDir.getCanonicalFile();
- } catch (IOException e) {
- // failed to look up canonical path, continue with original one
- }
- scanDirTracedLI(privilegedOdmAppDir,
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_VENDOR
- | SCAN_AS_PRIVILEGED,
- 0);
-
- // Collect ordinary odm packages. /odm is another vendor partition
- // other than /vendor.
- File odmAppDir = new File(Environment.getOdmDirectory(), "app");
- try {
- odmAppDir = odmAppDir.getCanonicalFile();
- } catch (IOException e) {
- // failed to look up canonical path, continue with original one
- }
- scanDirTracedLI(odmAppDir,
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_VENDOR,
- 0);
-
- // Collect all OEM packages.
- final File oemAppDir = new File(Environment.getOemDirectory(), "app");
- scanDirTracedLI(oemAppDir,
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_OEM,
- 0);
-
- // Collected privileged /product packages.
- File privilegedProductAppDir = new File(Environment.getProductDirectory(), "priv-app");
- try {
- privilegedProductAppDir = privilegedProductAppDir.getCanonicalFile();
- } catch (IOException e) {
- // failed to look up canonical path, continue with original one
- }
- scanDirTracedLI(privilegedProductAppDir,
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_PRODUCT
- | SCAN_AS_PRIVILEGED,
- 0);
-
- // Collect ordinary /product packages.
- File productAppDir = new File(Environment.getProductDirectory(), "app");
- try {
- productAppDir = productAppDir.getCanonicalFile();
- } catch (IOException e) {
- // failed to look up canonical path, continue with original one
- }
- scanDirTracedLI(productAppDir,
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_PRODUCT,
- 0);
-
- // Collected privileged /system_ext packages.
- File privilegedSystemExtAppDir =
- new File(Environment.getSystemExtDirectory(), "priv-app");
- try {
- privilegedSystemExtAppDir =
- privilegedSystemExtAppDir.getCanonicalFile();
- } catch (IOException e) {
- // failed to look up canonical path, continue with original one
- }
- scanDirTracedLI(privilegedSystemExtAppDir,
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_SYSTEM_EXT
- | SCAN_AS_PRIVILEGED,
- 0);
-
- // Collect ordinary /system_ext packages.
- File systemExtAppDir = new File(Environment.getSystemExtDirectory(), "app");
- try {
- systemExtAppDir = systemExtAppDir.getCanonicalFile();
- } catch (IOException e) {
- // failed to look up canonical path, continue with original one
+ for (int i = 0, size = SYSTEM_PARTITIONS.size(); i < size; i++) {
+ final SystemPartition partition = SYSTEM_PARTITIONS.get(i);
+ if (partition.privAppFolder != null) {
+ scanDirTracedLI(partition.privAppFolder, systemParseFlags,
+ systemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag, 0);
+ }
+ scanDirTracedLI(partition.appFolder, systemParseFlags,
+ systemScanFlags | partition.scanFlag, 0);
}
- scanDirTracedLI(systemExtAppDir,
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_SYSTEM_EXT,
- 0);
+
// Prune any system packages that no longer exist.
final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<>();
@@ -3151,89 +3026,26 @@ public class PackageManagerService extends IPackageManager.Stub
logCriticalInfo(Log.WARN, "Expected better " + packageName
+ " but never showed up; reverting to system");
- final @ParseFlags int reparseFlags;
- final @ScanFlags int rescanFlags;
- if (FileUtils.contains(privilegedAppDir, scanFile)) {
- reparseFlags =
- mDefParseFlags |
- PackageParser.PARSE_IS_SYSTEM_DIR;
- rescanFlags =
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_PRIVILEGED;
- } else if (FileUtils.contains(systemAppDir, scanFile)) {
- reparseFlags =
- mDefParseFlags |
- PackageParser.PARSE_IS_SYSTEM_DIR;
- rescanFlags =
- scanFlags
- | SCAN_AS_SYSTEM;
- } else if (FileUtils.contains(privilegedVendorAppDir, scanFile)
- || FileUtils.contains(privilegedOdmAppDir, scanFile)) {
- reparseFlags =
- mDefParseFlags |
- PackageParser.PARSE_IS_SYSTEM_DIR;
- rescanFlags =
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_VENDOR
- | SCAN_AS_PRIVILEGED;
- } else if (FileUtils.contains(vendorAppDir, scanFile)
- || FileUtils.contains(odmAppDir, scanFile)) {
- reparseFlags =
- mDefParseFlags |
- PackageParser.PARSE_IS_SYSTEM_DIR;
- rescanFlags =
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_VENDOR;
- } else if (FileUtils.contains(oemAppDir, scanFile)) {
- reparseFlags =
- mDefParseFlags |
- PackageParser.PARSE_IS_SYSTEM_DIR;
- rescanFlags =
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_OEM;
- } else if (FileUtils.contains(privilegedProductAppDir, scanFile)) {
- reparseFlags =
- mDefParseFlags |
- PackageParser.PARSE_IS_SYSTEM_DIR;
- rescanFlags =
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_PRODUCT
- | SCAN_AS_PRIVILEGED;
- } else if (FileUtils.contains(productAppDir, scanFile)) {
- reparseFlags =
- mDefParseFlags |
- PackageParser.PARSE_IS_SYSTEM_DIR;
- rescanFlags =
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_PRODUCT;
- } else if (FileUtils.contains(privilegedSystemExtAppDir, scanFile)) {
- reparseFlags =
- mDefParseFlags |
- PackageParser.PARSE_IS_SYSTEM_DIR;
- rescanFlags =
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_SYSTEM_EXT
- | SCAN_AS_PRIVILEGED;
- } else if (FileUtils.contains(systemExtAppDir, scanFile)) {
- reparseFlags =
- mDefParseFlags |
- PackageParser.PARSE_IS_SYSTEM_DIR;
- rescanFlags =
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_SYSTEM_EXT;
- } else {
+ @ParseFlags int reparseFlags = 0;
+ @ScanFlags int rescanFlags = 0;
+ for (int i1 = 0, size = SYSTEM_PARTITIONS.size(); i1 < size; i1++) {
+ SystemPartition partition = SYSTEM_PARTITIONS.get(i1);
+ if (partition.containsPrivApp(scanFile)) {
+ reparseFlags = systemParseFlags;
+ rescanFlags = systemScanFlags | SCAN_AS_PRIVILEGED
+ | partition.scanFlag;
+ break;
+ }
+ if (partition.containsApp(scanFile)) {
+ reparseFlags = systemParseFlags;
+ rescanFlags = systemScanFlags | partition.scanFlag;
+ break;
+ }
+ }
+ if (rescanFlags == 0) {
Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile);
continue;
}
-
mSettings.enableSystemPackageLPw(packageName);
try {
@@ -17124,8 +16936,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (new File(signaturePath).exists() && !VerityUtils.hasFsverity(filePath)) {
try {
VerityUtils.setUpFsverity(filePath, signaturePath);
- } catch (IOException | DigestException | NoSuchAlgorithmException
- | SecurityException e) {
+ } catch (IOException e) {
throw new PrepareFailure(PackageManager.INSTALL_FAILED_BAD_SIGNATURE,
"Failed to enable fs-verity: " + e);
}
@@ -17931,7 +17742,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);
}
}
@@ -18080,70 +17891,15 @@ public class PackageManagerService extends IPackageManager.Stub
}
static boolean locationIsPrivileged(String path) {
- try {
- final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
- final File privilegedVendorAppDir = new File(Environment.getVendorDirectory(), "priv-app");
- final File privilegedOdmAppDir = new File(Environment.getOdmDirectory(), "priv-app");
- final File privilegedProductAppDir = new File(Environment.getProductDirectory(), "priv-app");
- final File privilegedSystemExtAppDir =
- new File(Environment.getSystemExtDirectory(), "priv-app");
- return path.startsWith(privilegedAppDir.getCanonicalPath() + "/")
- || path.startsWith(privilegedVendorAppDir.getCanonicalPath() + "/")
- || path.startsWith(privilegedOdmAppDir.getCanonicalPath() + "/")
- || path.startsWith(privilegedProductAppDir.getCanonicalPath() + "/")
- || path.startsWith(privilegedSystemExtAppDir.getCanonicalPath() + "/");
- } catch (IOException e) {
- Slog.e(TAG, "Unable to access code path " + path);
- }
- return false;
- }
-
- static boolean locationIsOem(String path) {
- try {
- return path.startsWith(Environment.getOemDirectory().getCanonicalPath() + "/");
- } catch (IOException e) {
- Slog.e(TAG, "Unable to access code path " + path);
- }
- return false;
- }
-
- static boolean locationIsVendor(String path) {
- try {
- return path.startsWith(Environment.getVendorDirectory().getCanonicalPath() + "/")
- || path.startsWith(Environment.getOdmDirectory().getCanonicalPath() + "/");
- } catch (IOException e) {
- Slog.e(TAG, "Unable to access code path " + path);
- }
- return false;
- }
-
- static boolean locationIsProduct(String path) {
- try {
- return path.startsWith(Environment.getProductDirectory().getCanonicalPath() + "/");
- } catch (IOException e) {
- Slog.e(TAG, "Unable to access code path " + path);
- }
- return false;
- }
-
- static boolean locationIsSystemExt(String path) {
- try {
- return path.startsWith(
- Environment.getSystemExtDirectory().getCanonicalPath() + "/");
- } catch (IOException e) {
- Slog.e(TAG, "Unable to access code path " + path);
+ for (int i = 0, size = SYSTEM_PARTITIONS.size(); i < size; i++) {
+ SystemPartition partition = SYSTEM_PARTITIONS.get(i);
+ if (partition.containsPrivPath(path)) {
+ return true;
+ }
}
return false;
}
- static boolean locationIsOdm(String path) {
- try {
- return path.startsWith(Environment.getOdmDirectory().getCanonicalPath() + "/");
- } catch (IOException e) {
- Slog.e(TAG, "Unable to access code path " + path);
- }
- return false;
- }
/*
* Tries to delete system package.
@@ -18254,23 +18010,15 @@ public class PackageManagerService extends IPackageManager.Stub
| PackageParser.PARSE_MUST_BE_APK
| PackageParser.PARSE_IS_SYSTEM_DIR;
@ScanFlags int scanFlags = SCAN_AS_SYSTEM;
- if (locationIsPrivileged(codePathString)) {
- scanFlags |= SCAN_AS_PRIVILEGED;
- }
- if (locationIsOem(codePathString)) {
- scanFlags |= SCAN_AS_OEM;
- }
- if (locationIsVendor(codePathString)) {
- scanFlags |= SCAN_AS_VENDOR;
- }
- if (locationIsProduct(codePathString)) {
- scanFlags |= SCAN_AS_PRODUCT;
- }
- if (locationIsSystemExt(codePathString)) {
- scanFlags |= SCAN_AS_SYSTEM_EXT;
- }
- if (locationIsOdm(codePathString)) {
- scanFlags |= SCAN_AS_ODM;
+ for (int i = 0, size = SYSTEM_PARTITIONS.size(); i < size; i++) {
+ SystemPartition partition = SYSTEM_PARTITIONS.get(i);
+ if (partition.containsPath(codePathString)) {
+ scanFlags |= partition.scanFlag;
+ if (partition.containsPrivPath(codePathString)) {
+ scanFlags |= SCAN_AS_PRIVILEGED;
+ }
+ break;
+ }
}
final File codePath = new File(codePathString);
@@ -19787,7 +19535,7 @@ public class PackageManagerService extends IPackageManager.Stub
public void sendSessionCommitBroadcast(PackageInstaller.SessionInfo sessionInfo, int userId) {
UserManagerService ums = UserManagerService.getInstance();
- if (ums != null) {
+ if (ums != null && !sessionInfo.isStaged()) {
final UserInfo parent = ums.getProfileParent(userId);
final int launcherUid = (parent != null) ? parent.id : userId;
final ComponentName launcherComponent = getDefaultHomeActivity(launcherUid);
@@ -23380,11 +23128,24 @@ public class PackageManagerService extends IPackageManager.Stub
}
@Override
- public void grantEphemeralAccess(int userId, Intent intent,
- int targetAppId, int ephemeralAppId) {
+ public void grantImplicitAccess(int userId, Intent intent,
+ int callingAppId, int targetAppId) {
synchronized (mLock) {
- mInstantAppRegistry.grantInstantAccessLPw(userId, intent,
- targetAppId, ephemeralAppId);
+ final PackageParser.Package callingPackage = getPackage(
+ UserHandle.getUid(userId, callingAppId));
+ final PackageParser.Package targetPackage = getPackage(
+ UserHandle.getUid(userId, targetAppId));
+ if (callingPackage == null || targetPackage == null) {
+ return;
+ }
+
+ if (isInstantApp(callingPackage.packageName, userId)) {
+ mInstantAppRegistry.grantInstantAccessLPw(userId, intent,
+ callingAppId, targetAppId);
+ } else {
+ mAppsFilter.grantImplicitAccess(
+ callingPackage.packageName, targetPackage.packageName, userId);
+ }
}
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 597246884ca5..1873a4ec98d9 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -62,6 +62,7 @@ import android.os.PersistableBundle;
import android.os.Process;
import android.os.SELinux;
import android.os.SystemClock;
+import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManager;
@@ -94,6 +95,7 @@ import com.android.server.pm.permission.BasePermission;
import com.android.server.pm.permission.PermissionSettings;
import com.android.server.pm.permission.PermissionsState;
import com.android.server.pm.permission.PermissionsState.PermissionState;
+import com.android.server.utils.TimingsTraceAndSlog;
import libcore.io.IoUtils;
@@ -4012,8 +4014,11 @@ public final class Settings {
}
}
- void createNewUserLI(@NonNull PackageManagerService service, @NonNull Installer installer,
- int userHandle, String[] disallowedPackages) {
+ void createNewUserLI(@NonNull PackageManagerService service,
+ @NonNull Installer installer, int userHandle, String[] disallowedPackages) {
+ final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing",
+ Trace.TRACE_TAG_PACKAGE_MANAGER);
+ t.traceBegin("createNewUser-" + userHandle);
String[] volumeUuids;
String[] names;
int[] appIds;
@@ -4051,6 +4056,7 @@ public final class Settings {
targetSdkVersions[i] = ps.pkg.applicationInfo.targetSdkVersion;
}
}
+ t.traceBegin("createAppData");
for (int i = 0; i < packagesCount; i++) {
if (names[i] == null) {
continue;
@@ -4064,9 +4070,11 @@ public final class Settings {
Slog.w(TAG, "Failed to prepare app data", e);
}
}
+ t.traceEnd(); // createAppData
synchronized (mLock) {
applyDefaultPreferredAppsLPw(userHandle);
}
+ t.traceEnd(); // createNewUser
}
void removeUserLPw(int userId) {
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 3482e92a6fbd..6b4ef698a8f4 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -301,7 +301,7 @@ public class StagingManager {
// Greedily re-trigger the pre-reboot verification.
Slog.d(TAG, "Found pending staged session " + session.sessionId + " still to be "
+ "verified, resuming pre-reboot verification");
- mPreRebootVerificationHandler.startPreRebootVerification(session);
+ mPreRebootVerificationHandler.startPreRebootVerification(session.sessionId);
return;
}
if (!apexSessionInfo.isActivated && !apexSessionInfo.isSuccess) {
@@ -318,7 +318,7 @@ public class StagingManager {
// The APEX part of the session is activated, proceed with the installation of APKs.
try {
Slog.d(TAG, "Installing APK packages in session " + session.sessionId);
- installApksInSession(session, /* preReboot */ false);
+ installApksInSession(session);
} catch (PackageManagerException e) {
session.setStagedSessionFailed(e.error, e.getMessage());
@@ -410,72 +410,23 @@ public class StagingManager {
return apkSession;
}
- private void commitApkSession(@NonNull PackageInstallerSession apkSession,
- PackageInstallerSession originalSession, boolean preReboot)
- throws PackageManagerException {
- final int errorCode = preReboot ? SessionInfo.STAGED_SESSION_VERIFICATION_FAILED
- : SessionInfo.STAGED_SESSION_ACTIVATION_FAILED;
- if (preReboot) {
- final LocalIntentReceiverAsync receiver = new LocalIntentReceiverAsync(
- (Intent result) -> {
- int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
- PackageInstaller.STATUS_FAILURE);
- if (status != PackageInstaller.STATUS_SUCCESS) {
- final String errorMessage = result.getStringExtra(
- PackageInstaller.EXTRA_STATUS_MESSAGE);
- Slog.e(TAG, "Failure to install APK staged session "
- + originalSession.sessionId + " [" + errorMessage + "]");
- originalSession.setStagedSessionFailed(errorCode, errorMessage);
- return;
- }
- mPreRebootVerificationHandler.notifyPreRebootVerification_Apk_Complete(
- originalSession);
- });
- apkSession.commit(receiver.getIntentSender(), false);
- return;
- }
-
- if ((apkSession.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
- // If rollback is available for this session, notify the rollback
- // manager of the apk session so it can properly enable rollback.
- final IRollbackManager rm = IRollbackManager.Stub.asInterface(
- ServiceManager.getService(Context.ROLLBACK_SERVICE));
- try {
- rm.notifyStagedApkSession(originalSession.sessionId, apkSession.sessionId);
- } catch (RemoteException re) {
- Slog.e(TAG, "Failed to notifyStagedApkSession for session: "
- + originalSession.sessionId, re);
- }
- }
-
- final LocalIntentReceiverSync receiver = new LocalIntentReceiverSync();
- apkSession.commit(receiver.getIntentSender(), false);
- final Intent result = receiver.getResult();
- final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
- PackageInstaller.STATUS_FAILURE);
- if (status != PackageInstaller.STATUS_SUCCESS) {
- final String errorMessage = result.getStringExtra(
- PackageInstaller.EXTRA_STATUS_MESSAGE);
- Slog.e(TAG, "Failure to install APK staged session "
- + originalSession.sessionId + " [" + errorMessage + "]");
- throw new PackageManagerException(errorCode, errorMessage);
- }
- }
-
- private void installApksInSession(@NonNull PackageInstallerSession session,
- boolean preReboot) throws PackageManagerException {
+ /**
+ * Extract apks in the given session into a new session. Returns {@code null} if there is no
+ * apks in the given session. Only parent session is returned for multi-package session.
+ */
+ @Nullable
+ private PackageInstallerSession extractApksInSession(PackageInstallerSession session,
+ boolean preReboot) throws PackageManagerException {
final int errorCode = preReboot ? SessionInfo.STAGED_SESSION_VERIFICATION_FAILED
: SessionInfo.STAGED_SESSION_ACTIVATION_FAILED;
if (!session.isMultiPackage() && !isApexSession(session)) {
- // APK single-packaged staged session. Do a regular install.
- PackageInstallerSession apkSession = createAndWriteApkSession(session, preReboot);
- commitApkSession(apkSession, session, preReboot);
+ return createAndWriteApkSession(session, preReboot);
} else if (session.isMultiPackage()) {
// For multi-package staged sessions containing APKs, we identify which child sessions
// contain an APK, and with those then create a new multi-package group of sessions,
// carrying over all the session parameters and unmarking them as staged. On commit the
// sessions will be installed atomically.
- List<PackageInstallerSession> childSessions;
+ final List<PackageInstallerSession> childSessions;
synchronized (mStagedSessions) {
childSessions =
Arrays.stream(session.getChildSessionIds())
@@ -487,18 +438,18 @@ public class StagingManager {
}
if (childSessions.isEmpty()) {
// APEX-only multi-package staged session, nothing to do.
- return;
+ return null;
}
- PackageInstaller.SessionParams params = session.params.copy();
+ final PackageInstaller.SessionParams params = session.params.copy();
params.isStaged = false;
if (preReboot) {
params.installFlags &= ~PackageManager.INSTALL_ENABLE_ROLLBACK;
}
// TODO(b/129744602): use the userid from the original session.
- int apkParentSessionId = mPi.createSession(
+ final int apkParentSessionId = mPi.createSession(
params, session.getInstallerPackageName(),
0 /* UserHandle.SYSTEM */);
- PackageInstallerSession apkParentSession = mPi.getSession(apkParentSessionId);
+ final PackageInstallerSession apkParentSession = mPi.getSession(apkParentSessionId);
try {
apkParentSession.open();
} catch (IOException e) {
@@ -519,14 +470,80 @@ public class StagingManager {
"Failed to add a child session " + apkChildSession.sessionId);
}
}
- commitApkSession(apkParentSession, session, preReboot);
+ return apkParentSession;
+ }
+ return null;
+ }
+
+ private void verifyApksInSession(PackageInstallerSession session)
+ throws PackageManagerException {
+
+ final PackageInstallerSession apksToVerify = extractApksInSession(
+ session, /* preReboot */ true);
+ if (apksToVerify == null) {
+ return;
+ }
+
+ final LocalIntentReceiverAsync receiver = new LocalIntentReceiverAsync(
+ (Intent result) -> {
+ int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+ PackageInstaller.STATUS_FAILURE);
+ if (status != PackageInstaller.STATUS_SUCCESS) {
+ final String errorMessage = result.getStringExtra(
+ PackageInstaller.EXTRA_STATUS_MESSAGE);
+ Slog.e(TAG, "Failure to verify APK staged session "
+ + session.sessionId + " [" + errorMessage + "]");
+ session.setStagedSessionFailed(
+ SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, errorMessage);
+ return;
+ }
+ mPreRebootVerificationHandler.notifyPreRebootVerification_Apk_Complete(
+ session.sessionId);
+ });
+
+ apksToVerify.commit(receiver.getIntentSender(), false);
+ }
+
+ private void installApksInSession(@NonNull PackageInstallerSession session)
+ throws PackageManagerException {
+
+ final PackageInstallerSession apksToInstall = extractApksInSession(
+ session, /* preReboot */ false);
+ if (apksToInstall == null) {
+ return;
+ }
+
+ if ((apksToInstall.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
+ // If rollback is available for this session, notify the rollback
+ // manager of the apk session so it can properly enable rollback.
+ final IRollbackManager rm = IRollbackManager.Stub.asInterface(
+ ServiceManager.getService(Context.ROLLBACK_SERVICE));
+ try {
+ rm.notifyStagedApkSession(session.sessionId, apksToInstall.sessionId);
+ } catch (RemoteException re) {
+ Slog.e(TAG, "Failed to notifyStagedApkSession for session: "
+ + session.sessionId, re);
+ }
+ }
+
+ final LocalIntentReceiverSync receiver = new LocalIntentReceiverSync();
+ apksToInstall.commit(receiver.getIntentSender(), false);
+ final Intent result = receiver.getResult();
+ final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+ PackageInstaller.STATUS_FAILURE);
+ if (status != PackageInstaller.STATUS_SUCCESS) {
+ final String errorMessage = result.getStringExtra(
+ PackageInstaller.EXTRA_STATUS_MESSAGE);
+ Slog.e(TAG, "Failure to install APK staged session "
+ + session.sessionId + " [" + errorMessage + "]");
+ throw new PackageManagerException(
+ SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMessage);
}
- // APEX single-package staged session, nothing to do.
}
void commitSession(@NonNull PackageInstallerSession session) {
updateStoredSession(session);
- mPreRebootVerificationHandler.startPreRebootVerification(session);
+ mPreRebootVerificationHandler.startPreRebootVerification(session.sessionId);
}
@Nullable
@@ -653,7 +670,7 @@ public class StagingManager {
if (!session.isStagedSessionReady()) {
// The framework got restarted before the pre-reboot verification could complete,
// restart the verification.
- mPreRebootVerificationHandler.startPreRebootVerification(session);
+ mPreRebootVerificationHandler.startPreRebootVerification(session.sessionId);
} else {
// Session had already being marked ready. Start the checks to verify if there is any
// follow-up work.
@@ -737,7 +754,16 @@ public class StagingManager {
@Override
public void handleMessage(Message msg) {
- PackageInstallerSession session = (PackageInstallerSession) msg.obj;
+ final int sessionId = msg.arg1;
+ final PackageInstallerSession session;
+ synchronized (mStagedSessions) {
+ session = mStagedSessions.get(sessionId);
+ }
+ // Maybe session was aborted before pre-reboot verification was complete
+ if (session == null) {
+ Slog.d(TAG, "Stopping pre-reboot verification for sessionId: " + sessionId);
+ return;
+ }
switch (msg.what) {
case MSG_PRE_REBOOT_VERIFICATION_START:
handlePreRebootVerification_Start(session);
@@ -755,20 +781,20 @@ public class StagingManager {
}
// Method for starting the pre-reboot verification
- private void startPreRebootVerification(PackageInstallerSession session) {
- obtainMessage(MSG_PRE_REBOOT_VERIFICATION_START, session).sendToTarget();
+ private void startPreRebootVerification(int sessionId) {
+ obtainMessage(MSG_PRE_REBOOT_VERIFICATION_START, sessionId, 0).sendToTarget();
}
- private void notifyPreRebootVerification_Start_Complete(PackageInstallerSession session) {
- obtainMessage(MSG_PRE_REBOOT_VERIFICATION_APEX, session).sendToTarget();
+ private void notifyPreRebootVerification_Start_Complete(int sessionId) {
+ obtainMessage(MSG_PRE_REBOOT_VERIFICATION_APEX, sessionId, 0).sendToTarget();
}
- private void notifyPreRebootVerification_Apex_Complete(PackageInstallerSession session) {
- obtainMessage(MSG_PRE_REBOOT_VERIFICATION_APK, session).sendToTarget();
+ private void notifyPreRebootVerification_Apex_Complete(int sessionId) {
+ obtainMessage(MSG_PRE_REBOOT_VERIFICATION_APK, sessionId, 0).sendToTarget();
}
- private void notifyPreRebootVerification_Apk_Complete(PackageInstallerSession session) {
- obtainMessage(MSG_PRE_REBOOT_VERIFICATION_END, session).sendToTarget();
+ private void notifyPreRebootVerification_Apk_Complete(int sessionId) {
+ obtainMessage(MSG_PRE_REBOOT_VERIFICATION_END, sessionId, 0).sendToTarget();
}
/**
@@ -778,7 +804,7 @@ public class StagingManager {
*/
private void handlePreRebootVerification_Start(@NonNull PackageInstallerSession session) {
Slog.d(TAG, "Starting preRebootVerification for session " + session.sessionId);
- notifyPreRebootVerification_Start_Complete(session);
+ notifyPreRebootVerification_Start_Complete(session.sessionId);
}
/**
@@ -808,7 +834,7 @@ public class StagingManager {
}
}
- notifyPreRebootVerification_Apex_Complete(session);
+ notifyPreRebootVerification_Apex_Complete(session.sessionId);
}
/**
@@ -819,7 +845,7 @@ public class StagingManager {
*/
private void handlePreRebootVerification_Apk(@NonNull PackageInstallerSession session) {
if (!sessionContainsApk(session)) {
- notifyPreRebootVerification_Apk_Complete(session);
+ notifyPreRebootVerification_Apk_Complete(session.sessionId);
return;
}
@@ -827,8 +853,8 @@ public class StagingManager {
Slog.d(TAG, "Running a pre-reboot verification for APKs in session "
+ session.sessionId + " by performing a dry-run install");
- // installApksInSession will notify the handler when APK verification is complete
- installApksInSession(session, /* preReboot */ true);
+ // verifyApksInSession will notify the handler when APK verification is complete
+ verifyApksInSession(session);
// TODO(b/118865310): abort the session on apexd.
} catch (PackageManagerException e) {
session.setStagedSessionFailed(e.error, e.getMessage());
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 81723cbe22c5..9371c4473bb3 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);
@@ -2712,14 +2713,25 @@ public class UserManagerService extends IUserManager.Stub {
return createUserInternalUnchecked(name, flags, parentId, disallowedPackages);
}
- private UserInfo createUserInternalUnchecked(String name, int flags, int parentId,
- String[] disallowedPackages) {
+ private UserInfo createUserInternalUnchecked(@Nullable String name, int flags,
+ int parentId, @Nullable String[] disallowedPackages) {
+ TimingsTraceAndSlog t = new TimingsTraceAndSlog();
+ t.traceBegin("createUser");
+ UserInfo userInfo =
+ createUserInternalUncheckedNoTracing(name, flags, parentId, disallowedPackages, t);
+ t.traceEnd();
+ return userInfo;
+ }
+
+ private UserInfo createUserInternalUncheckedNoTracing(@Nullable String name, int flags,
+ int parentId, @Nullable String[] disallowedPackages, @NonNull TimingsTraceAndSlog t) {
DeviceStorageMonitorInternal dsm = LocalServices
.getService(DeviceStorageMonitorInternal.class);
if (dsm.isMemoryLow()) {
Log.w(LOG_TAG, "Cannot add user. Not enough space on disk.");
return null;
}
+
final boolean isGuest = (flags & UserInfo.FLAG_GUEST) != 0;
final boolean isManagedProfile = (flags & UserInfo.FLAG_MANAGED_PROFILE) != 0;
final boolean isRestricted = (flags & UserInfo.FLAG_RESTRICTED) != 0;
@@ -2770,17 +2782,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;
@@ -2829,11 +2831,21 @@ public class UserManagerService extends IUserManager.Stub {
}
}
}
+
+ t.traceBegin("createUserKey");
final StorageManager storage = mContext.getSystemService(StorageManager.class);
storage.createUserKey(userId, userInfo.serialNumber, userInfo.isEphemeral());
+ t.traceEnd();
+
+ t.traceBegin("prepareUserData");
mUserDataPreparer.prepareUserData(userId, userInfo.serialNumber,
StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
+ t.traceEnd();
+
+ t.traceBegin("PM.createNewUser");
mPm.createNewUser(userId, disallowedPackages);
+ t.traceEnd();
+
userInfo.partial = false;
synchronized (mPackagesLock) {
writeUserLP(userData);
@@ -2848,7 +2860,11 @@ public class UserManagerService extends IUserManager.Stub {
synchronized (mRestrictionsLock) {
mBaseUserRestrictions.append(userId, restrictions);
}
+
+ t.traceBegin("PM.onNewUserCreated");
mPm.onNewUserCreated(userId);
+ t.traceEnd();
+
Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED);
addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL,
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/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index e57f43685108..eb648b33c4ca 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -542,6 +542,10 @@ public final class PowerManagerService extends SystemService
// True if we in the process of performing a forceSuspend
private boolean mForceSuspendActive;
+ // Transition to Doze is in progress. We have transitioned to WAKEFULNESS_DOZING,
+ // but the DreamService has not yet been told to start (it's an async process).
+ private boolean mDozeStartInProgress;
+
private final class ForegroundProfileObserver extends SynchronousUserSwitchObserver {
@Override
public void onUserSwitching(int newUserId) throws RemoteException {}
@@ -1514,6 +1518,7 @@ public final class PowerManagerService extends SystemService
mLastSleepTime = eventTime;
mLastSleepReason = reason;
mSandmanSummoned = true;
+ mDozeStartInProgress = true;
setWakefulnessLocked(WAKEFULNESS_DOZING, reason, eventTime);
// Report the number of wake locks that will be cleared by going to sleep.
@@ -1601,6 +1606,10 @@ public final class PowerManagerService extends SystemService
mWakefulness = wakefulness;
mWakefulnessChanging = true;
mDirty |= DIRTY_WAKEFULNESS;
+
+ // This is only valid while we are in wakefulness dozing. Set to false otherwise.
+ mDozeStartInProgress &= (mWakefulness == WAKEFULNESS_DOZING);
+
if (mNotifier != null) {
mNotifier.onWakefulnessChangeStarted(wakefulness, reason, eventTime);
}
@@ -1631,6 +1640,9 @@ public final class PowerManagerService extends SystemService
if (mWakefulness == WAKEFULNESS_DOZING
&& (mWakeLockSummary & WAKE_LOCK_DOZE) == 0) {
return; // wait until dream has enabled dozing
+ } else {
+ // Doze wakelock acquired (doze started) or device is no longer dozing.
+ mDozeStartInProgress = false;
}
if (mWakefulness == WAKEFULNESS_DOZING || mWakefulness == WAKEFULNESS_ASLEEP) {
logSleepTimeoutRecapturedLocked();
@@ -2309,6 +2321,10 @@ public final class PowerManagerService extends SystemService
isDreaming = false;
}
+ // At this point, we either attempted to start the dream or no attempt will be made,
+ // so stop holding the display suspend blocker for Doze.
+ mDozeStartInProgress = false;
+
// Update dream state.
synchronized (mLock) {
// Remember the initial battery level when the dream started.
@@ -2734,6 +2750,16 @@ public final class PowerManagerService extends SystemService
if (mScreenBrightnessBoostInProgress) {
return true;
}
+
+ // When we transition to DOZING, we have to keep the display suspend blocker
+ // up until the Doze service has a change to acquire the DOZE wakelock.
+ // Here we wait for mWakefulnessChanging to become false since the wakefulness
+ // transition to DOZING isn't considered "changed" until the doze wake lock is
+ // acquired.
+ if (mWakefulness == WAKEFULNESS_DOZING && mDozeStartInProgress) {
+ return true;
+ }
+
// Let the system suspend if the screen is off or dozing.
return false;
}
diff --git a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
index cae09ea37f2a..3f9cc83b53d8 100644
--- a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
+++ b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
@@ -23,17 +23,12 @@ import android.util.IntArray;
import android.util.Slog;
import android.util.SparseLongArray;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.pm.Installer;
import com.android.server.pm.Installer.InstallerException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
import java.util.List;
-import java.util.Map;
-import java.util.Set;
/**
* Encapsulates the logic for initiating userdata snapshots and rollbacks via installd.
@@ -56,6 +51,8 @@ public class AppDataRollbackHelper {
* {@code userIds}. Updates said {@code packageRollbackInfo} with the inodes of the CE user data
* snapshot folders.
*/
+ @GuardedBy("rollback.getLock")
+ // TODO(b/136241838): Move into Rollback and synchronize there.
public void snapshotAppData(
int snapshotId, PackageRollbackInfo packageRollbackInfo, int[] userIds) {
for (int user : userIds) {
@@ -92,6 +89,8 @@ public class AppDataRollbackHelper {
* to {@code packageRollbackInfo} are restricted to the removal or addition of {@code
* userId} to the list of pending backups or restores.
*/
+ @GuardedBy("rollback.getLock")
+ // TODO(b/136241838): Move into Rollback and synchronize there.
public boolean restoreAppData(int rollbackId, PackageRollbackInfo packageRollbackInfo,
int userId, int appId, String seInfo) {
int storageFlags = Installer.FLAG_STORAGE_DE;
@@ -135,6 +134,8 @@ public class AppDataRollbackHelper {
* Deletes an app data snapshot with a given {@code rollbackId} for a specified package
* {@code packageName} for a given {@code user}.
*/
+ @GuardedBy("rollback.getLock")
+ // TODO(b/136241838): Move into Rollback and synchronize there.
public void destroyAppDataSnapshot(int rollbackId, PackageRollbackInfo packageRollbackInfo,
int user) {
int storageFlags = Installer.FLAG_STORAGE_DE;
@@ -156,141 +157,68 @@ public class AppDataRollbackHelper {
}
/**
- * Computes the list of pending backups for {@code userId} given lists of rollbacks.
- * Packages pending backup for the given user are added to {@code pendingBackupPackages} along
- * with their corresponding {@code PackageRollbackInfo}.
+ * Commits the pending backups and restores for a given {@code userId} and {@code rollback}. If
+ * the rollback has a pending backup, it is updated with a mapping from {@code userId} to inode
+ * of the CE user data snapshot.
*
- * @return the list of rollbacks that have pending backups. Note that some of the
- * backups won't be performed, because they might be counteracted by pending restores.
+ * @return true if any backups or restores were found for the userId
*/
- private static List<Rollback> computePendingBackups(int userId,
- Map<String, PackageRollbackInfo> pendingBackupPackages,
- List<Rollback> rollbacks) {
- List<Rollback> rollbacksWithPendingBackups = new ArrayList<>();
-
- for (Rollback rollback : rollbacks) {
- for (PackageRollbackInfo info : rollback.info.getPackages()) {
- final IntArray pendingBackupUsers = info.getPendingBackups();
- if (pendingBackupUsers != null) {
- final int idx = pendingBackupUsers.indexOf(userId);
- if (idx != -1) {
- pendingBackupPackages.put(info.getPackageName(), info);
- if (rollbacksWithPendingBackups.indexOf(rollback) == -1) {
- rollbacksWithPendingBackups.add(rollback);
- }
- }
+ @GuardedBy("rollback.getLock")
+ boolean commitPendingBackupAndRestoreForUser(int userId, Rollback rollback) {
+ boolean foundBackupOrRestore = false;
+ for (PackageRollbackInfo info : rollback.info.getPackages()) {
+ boolean hasPendingBackup = false;
+ boolean hasPendingRestore = false;
+ final IntArray pendingBackupUsers = info.getPendingBackups();
+ if (pendingBackupUsers != null) {
+ if (pendingBackupUsers.indexOf(userId) != -1) {
+ hasPendingBackup = true;
+ foundBackupOrRestore = true;
}
}
- }
- return rollbacksWithPendingBackups;
- }
-
- /**
- * Computes the list of pending restores for {@code userId} given lists of rollbacks.
- * Packages pending restore are added to {@code pendingRestores} along with their corresponding
- * {@code PackageRollbackInfo}.
- *
- * @return the list of rollbacks that have pending restores. Note that some of the
- * restores won't be performed, because they might be counteracted by pending backups.
- */
- private static List<Rollback> computePendingRestores(int userId,
- Map<String, PackageRollbackInfo> pendingRestorePackages,
- List<Rollback> rollbacks) {
- List<Rollback> rollbacksWithPendingRestores = new ArrayList<>();
- for (Rollback rollback : rollbacks) {
- for (PackageRollbackInfo info : rollback.info.getPackages()) {
- final RestoreInfo ri = info.getRestoreInfo(userId);
- if (ri != null) {
- pendingRestorePackages.put(info.getPackageName(), info);
- if (rollbacksWithPendingRestores.indexOf(rollback) == -1) {
- rollbacksWithPendingRestores.add(rollback);
- }
- }
+ RestoreInfo ri = info.getRestoreInfo(userId);
+ if (ri != null) {
+ hasPendingRestore = true;
+ foundBackupOrRestore = true;
}
- }
-
- return rollbacksWithPendingRestores;
- }
-
- /**
- * Commits the list of pending backups and restores for a given {@code userId}. For rollbacks
- * with pending backups, updates the {@code Rollback} instance with a mapping from
- * {@code userId} to inode of the CE user data snapshot.
- *
- * @return the set of rollbacks with changes that should be stored on disk.
- */
- public Set<Rollback> commitPendingBackupAndRestoreForUser(int userId,
- List<Rollback> rollbacks) {
- final Map<String, PackageRollbackInfo> pendingBackupPackages = new HashMap<>();
- final List<Rollback> pendingBackups = computePendingBackups(userId,
- pendingBackupPackages, rollbacks);
-
- final Map<String, PackageRollbackInfo> pendingRestorePackages = new HashMap<>();
- final List<Rollback> pendingRestores = computePendingRestores(userId,
- pendingRestorePackages, rollbacks);
-
- // First remove unnecessary backups, i.e. when user did not unlock their phone between the
- // request to backup data and the request to restore it.
- Iterator<Map.Entry<String, PackageRollbackInfo>> iter =
- pendingBackupPackages.entrySet().iterator();
- while (iter.hasNext()) {
- PackageRollbackInfo backupPackage = iter.next().getValue();
- PackageRollbackInfo restorePackage =
- pendingRestorePackages.get(backupPackage.getPackageName());
- if (restorePackage != null) {
- backupPackage.removePendingBackup(userId);
- backupPackage.removePendingRestoreInfo(userId);
- iter.remove();
- pendingRestorePackages.remove(backupPackage.getPackageName());
+ if (hasPendingBackup && hasPendingRestore) {
+ // Remove unnecessary backup, i.e. when user did not unlock their phone between the
+ // request to backup data and the request to restore it.
+ info.removePendingBackup(userId);
+ info.removePendingRestoreInfo(userId);
+ continue;
}
- }
- if (!pendingBackupPackages.isEmpty()) {
- for (Rollback rollback : pendingBackups) {
- for (PackageRollbackInfo info : rollback.info.getPackages()) {
- final IntArray pendingBackupUsers = info.getPendingBackups();
- final int idx = pendingBackupUsers.indexOf(userId);
- if (idx != -1) {
- try {
- long ceSnapshotInode = mInstaller.snapshotAppData(info.getPackageName(),
- userId, rollback.info.getRollbackId(),
- Installer.FLAG_STORAGE_CE);
- info.putCeSnapshotInode(userId, ceSnapshotInode);
- pendingBackupUsers.remove(idx);
- } catch (InstallerException ie) {
- Slog.e(TAG,
- "Unable to create app data snapshot for: "
+ if (hasPendingBackup) {
+ int idx = pendingBackupUsers.indexOf(userId);
+ try {
+ long ceSnapshotInode = mInstaller.snapshotAppData(info.getPackageName(),
+ userId, rollback.info.getRollbackId(),
+ Installer.FLAG_STORAGE_CE);
+ info.putCeSnapshotInode(userId, ceSnapshotInode);
+ pendingBackupUsers.remove(idx);
+ } catch (InstallerException ie) {
+ Slog.e(TAG,
+ "Unable to create app data snapshot for: "
+ info.getPackageName() + ", userId: " + userId, ie);
- }
- }
}
}
- }
- if (!pendingRestorePackages.isEmpty()) {
- for (Rollback rollback : pendingRestores) {
- for (PackageRollbackInfo info : rollback.info.getPackages()) {
- final RestoreInfo ri = info.getRestoreInfo(userId);
- if (ri != null) {
- try {
- mInstaller.restoreAppDataSnapshot(info.getPackageName(), ri.appId,
- ri.seInfo, userId, rollback.info.getRollbackId(),
- Installer.FLAG_STORAGE_CE);
- info.removeRestoreInfo(ri);
- } catch (InstallerException ie) {
- Slog.e(TAG, "Unable to restore app data snapshot for: "
- + info.getPackageName(), ie);
- }
- }
+ if (hasPendingRestore) {
+ try {
+ mInstaller.restoreAppDataSnapshot(info.getPackageName(), ri.appId,
+ ri.seInfo, userId, rollback.info.getRollbackId(),
+ Installer.FLAG_STORAGE_CE);
+ info.removeRestoreInfo(ri);
+ } catch (InstallerException ie) {
+ Slog.e(TAG, "Unable to restore app data snapshot for: "
+ + info.getPackageName(), ie);
}
}
}
-
- final Set<Rollback> changed = new HashSet<>(pendingBackups);
- changed.addAll(pendingRestores);
- return changed;
+ return foundBackupOrRestore;
}
/**
diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java
index 6769fe07bbf8..2dc495197254 100644
--- a/services/core/java/com/android/server/rollback/Rollback.java
+++ b/services/core/java/com/android/server/rollback/Rollback.java
@@ -18,19 +18,27 @@ package com.android.server.rollback;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.content.rollback.PackageRollbackInfo;
import android.content.rollback.RollbackInfo;
+import com.android.internal.annotations.GuardedBy;
+
import java.io.File;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.text.ParseException;
import java.time.Instant;
import java.util.ArrayList;
+import java.util.List;
/**
- * Information about a rollback available for a set of atomically installed
- * packages.
+ * Information about a rollback available for a set of atomically installed packages.
+ *
+ * <p>When accessing the state of a Rollback object, the caller is responsible for synchronization.
+ * The lock object provided by {@link #getLock} should be acquired when accessing any of the mutable
+ * state of a Rollback, including from the {@link RollbackInfo} and any of the
+ * {@link PackageRollbackInfo} objects held within.
*/
class Rollback {
@IntDef(flag = true, prefix = { "ROLLBACK_STATE_" }, value = {
@@ -58,8 +66,18 @@ class Rollback {
static final int ROLLBACK_STATE_COMMITTED = 3;
/**
+ * The session ID for the staged session if this rollback data represents a staged session,
+ * {@code -1} otherwise.
+ */
+ private final int mStagedSessionId;
+
+ /**
* The rollback info for this rollback.
+ *
+ * <p>Any access to this field that touches any mutable state should be synchronized on
+ * {@link #getLock}.
*/
+ @GuardedBy("getLock")
public final RollbackInfo info;
/**
@@ -74,23 +92,20 @@ 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.
*/
+ @GuardedBy("mLock")
private @NonNull Instant mTimestamp;
/**
- * The session ID for the staged session if this rollback data represents a staged session,
- * {@code -1} otherwise.
- */
- private final int mStagedSessionId;
-
- /**
* The current state of the rollback.
* ENABLING, AVAILABLE, or COMMITTED.
*/
+ @GuardedBy("mLock")
private @RollbackState int mState;
/**
* The id of the post-reboot apk session for a staged install, if any.
*/
+ @GuardedBy("mLock")
private int mApkSessionId = -1;
/**
@@ -98,10 +113,17 @@ class Rollback {
* for this rollback because it has just been committed but the rollback
* has not yet been fully applied.
*/
- // NOTE: All accesses to this field are from the RollbackManager handler thread.
+ @GuardedBy("mLock")
private boolean mRestoreUserDataInProgress = false;
/**
+ * Lock object to guard all access to Rollback state.
+ *
+ * @see #getLock
+ */
+ private final Object mLock = new Object();
+
+ /**
* Constructs a new, empty Rollback instance.
*
* @param rollbackId the id of the rollback.
@@ -135,8 +157,23 @@ class Rollback {
}
/**
+ * Returns a lock object that should be acquired before accessing any Rollback state from
+ * {@link RollbackManagerServiceImpl}.
+ *
+ * <p>Note that while holding this lock, the lock for {@link RollbackManagerServiceImpl} should
+ * not be acquired (but it is ok to acquire this lock while already holding the lock for that
+ * class).
+ */
+ // TODO(b/136241838): Move rollback functionality into this class and synchronize on the lock
+ // internally. Remove this method once this has been done for all cases.
+ Object getLock() {
+ return mLock;
+ }
+
+ /**
* Whether the rollback is for rollback of a staged install.
*/
+ @GuardedBy("getLock")
boolean isStaged() {
return info.isStaged();
}
@@ -151,6 +188,7 @@ class Rollback {
/**
* Returns the time when the upgrade occurred, for purposes of expiring rollback data.
*/
+ @GuardedBy("getLock")
Instant getTimestamp() {
return mTimestamp;
}
@@ -158,6 +196,7 @@ class Rollback {
/**
* Sets the time at which upgrade occurred.
*/
+ @GuardedBy("getLock")
void setTimestamp(Instant timestamp) {
mTimestamp = timestamp;
}
@@ -173,6 +212,7 @@ class Rollback {
/**
* Returns true if the rollback is in the ENABLING state.
*/
+ @GuardedBy("getLock")
boolean isEnabling() {
return mState == ROLLBACK_STATE_ENABLING;
}
@@ -180,6 +220,7 @@ class Rollback {
/**
* Returns true if the rollback is in the AVAILABLE state.
*/
+ @GuardedBy("getLock")
boolean isAvailable() {
return mState == ROLLBACK_STATE_AVAILABLE;
}
@@ -187,6 +228,7 @@ class Rollback {
/**
* Returns true if the rollback is in the COMMITTED state.
*/
+ @GuardedBy("getLock")
boolean isCommitted() {
return mState == ROLLBACK_STATE_COMMITTED;
}
@@ -194,6 +236,7 @@ class Rollback {
/**
* Sets the state of the rollback to AVAILABLE.
*/
+ @GuardedBy("getLock")
void setAvailable() {
mState = ROLLBACK_STATE_AVAILABLE;
}
@@ -201,6 +244,7 @@ class Rollback {
/**
* Sets the state of the rollback to COMMITTED.
*/
+ @GuardedBy("getLock")
void setCommitted() {
mState = ROLLBACK_STATE_COMMITTED;
}
@@ -208,6 +252,7 @@ class Rollback {
/**
* Returns the id of the post-reboot apk session for a staged install, if any.
*/
+ @GuardedBy("getLock")
int getApkSessionId() {
return mApkSessionId;
}
@@ -215,6 +260,7 @@ class Rollback {
/**
* Sets the id of the post-reboot apk session for a staged install.
*/
+ @GuardedBy("getLock")
void setApkSessionId(int apkSessionId) {
mApkSessionId = apkSessionId;
}
@@ -223,6 +269,7 @@ class Rollback {
* 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.
*/
+ @GuardedBy("getLock")
boolean isRestoreUserDataInProgress() {
return mRestoreUserDataInProgress;
}
@@ -231,10 +278,65 @@ class Rollback {
* 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.
*/
+ @GuardedBy("getLock")
void setRestoreUserDataInProgress(boolean restoreUserDataInProgress) {
mRestoreUserDataInProgress = restoreUserDataInProgress;
}
+ /**
+ * Returns true if this rollback includes the package with the provided {@code packageName}.
+ */
+ @GuardedBy("getLock")
+ boolean includesPackage(String packageName) {
+ for (PackageRollbackInfo info : info.getPackages()) {
+ if (info.getPackageName().equals(packageName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if this rollback includes the package with the provided {@code packageName}
+ * with a <i>version rolled back from</i> that is not {@code versionCode}.
+ */
+ @GuardedBy("getLock")
+ boolean includesPackageWithDifferentVersion(String packageName, long versionCode) {
+ for (PackageRollbackInfo info : info.getPackages()) {
+ if (info.getPackageName().equals(packageName)
+ && info.getVersionRolledBackFrom().getLongVersionCode() != versionCode) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns a list containing the names of all the packages included in this rollback.
+ */
+ @GuardedBy("getLock")
+ List<String> getPackageNames() {
+ List<String> result = new ArrayList<>();
+ for (PackageRollbackInfo info : info.getPackages()) {
+ result.add(info.getPackageName());
+ }
+ return result;
+ }
+
+ /**
+ * Returns a list containing the names of all the apex packages included in this rollback.
+ */
+ @GuardedBy("getLock")
+ List<String> getApexPackageNames() {
+ List<String> result = new ArrayList<>();
+ for (PackageRollbackInfo info : info.getPackages()) {
+ if (info.isApex()) {
+ result.add(info.getPackageName());
+ }
+ }
+ return result;
+ }
+
static String rollbackStateToString(@RollbackState int state) {
switch (state) {
case Rollback.ROLLBACK_STATE_ENABLING: return "enabling";
@@ -254,6 +356,7 @@ class Rollback {
throw new ParseException("Invalid rollback state: " + state, 0);
}
+ @GuardedBy("getLock")
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 96d284bb1c58..e8e448aa118e 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -282,8 +282,10 @@ 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.isAvailable()) {
- rollbacks.add(rollback.info);
+ synchronized (rollback.getLock()) {
+ if (rollback.isAvailable()) {
+ rollbacks.add(rollback.info);
+ }
}
}
return new ParceledListSlice<>(rollbacks);
@@ -298,8 +300,10 @@ 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.isCommitted()) {
- rollbacks.add(rollback.info);
+ synchronized (rollback.getLock()) {
+ if (rollback.isCommitted()) {
+ rollbacks.add(rollback.info);
+ }
}
}
return new ParceledListSlice<>(rollbacks);
@@ -332,8 +336,11 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
Iterator<Rollback> iter = mRollbacks.iterator();
while (iter.hasNext()) {
Rollback rollback = iter.next();
- rollback.setTimestamp(rollback.getTimestamp().plusMillis(timeDifference));
- saveRollback(rollback);
+ synchronized (rollback.getLock()) {
+ rollback.setTimestamp(
+ rollback.getTimestamp().plusMillis(timeDifference));
+ saveRollback(rollback);
+ }
}
}
}
@@ -358,86 +365,94 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
Slog.i(TAG, "Initiating rollback");
Rollback rollback = getRollbackForId(rollbackId);
- if (rollback == null || !rollback.isAvailable()) {
+ if (rollback == null) {
sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE_ROLLBACK_UNAVAILABLE,
"Rollback unavailable");
return;
}
-
- // Get a context for the caller to use to install the downgraded
- // version of the package.
- final Context context;
- try {
- context = mContext.createPackageContext(callerPackageName, 0);
- } catch (PackageManager.NameNotFoundException e) {
- sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE,
- "Invalid callerPackageName");
- return;
- }
-
- PackageManager pm = context.getPackageManager();
- try {
- PackageInstaller packageInstaller = pm.getPackageInstaller();
- PackageInstaller.SessionParams parentParams = new PackageInstaller.SessionParams(
- PackageInstaller.SessionParams.MODE_FULL_INSTALL);
- parentParams.setRequestDowngrade(true);
- parentParams.setMultiPackage();
- if (rollback.isStaged()) {
- parentParams.setStaged();
+ synchronized (rollback.getLock()) {
+ if (!rollback.isAvailable()) {
+ sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE_ROLLBACK_UNAVAILABLE,
+ "Rollback unavailable");
+ return;
}
- int parentSessionId = packageInstaller.createSession(parentParams);
- PackageInstaller.Session parentSession = packageInstaller.openSession(parentSessionId);
+ // Get a context for the caller to use to install the downgraded
+ // version of the package.
+ final Context context;
+ try {
+ context = mContext.createPackageContext(callerPackageName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE,
+ "Invalid callerPackageName");
+ return;
+ }
- for (PackageRollbackInfo info : rollback.info.getPackages()) {
- PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
+ PackageManager pm = context.getPackageManager();
+ try {
+ PackageInstaller packageInstaller = pm.getPackageInstaller();
+ PackageInstaller.SessionParams parentParams = new PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
- // TODO: We can't get the installerPackageName for apex
- // (b/123920130). Is it okay to ignore the installer package
- // for apex?
- if (!info.isApex()) {
- String installerPackageName = pm.getInstallerPackageName(info.getPackageName());
- if (installerPackageName != null) {
- params.setInstallerPackageName(installerPackageName);
- }
- }
- params.setRequestDowngrade(true);
- params.setRequiredInstalledVersionCode(
- info.getVersionRolledBackFrom().getLongVersionCode());
+ parentParams.setRequestDowngrade(true);
+ parentParams.setMultiPackage();
if (rollback.isStaged()) {
- params.setStaged();
- }
- if (info.isApex()) {
- params.setInstallAsApex();
- }
- int sessionId = packageInstaller.createSession(params);
- PackageInstaller.Session session = packageInstaller.openSession(sessionId);
- File[] packageCodePaths = RollbackStore.getPackageCodePaths(
- rollback, info.getPackageName());
- if (packageCodePaths == null) {
- sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE,
- "Backup copy of package inaccessible");
- return;
+ parentParams.setStaged();
}
- for (File packageCodePath : packageCodePaths) {
- try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(packageCodePath,
+ int parentSessionId = packageInstaller.createSession(parentParams);
+ PackageInstaller.Session parentSession = packageInstaller.openSession(
+ parentSessionId);
+
+ for (PackageRollbackInfo info : rollback.info.getPackages()) {
+ PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
+ PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+ // TODO: We can't get the installerPackageName for apex
+ // (b/123920130). Is it okay to ignore the installer package
+ // for apex?
+ if (!info.isApex()) {
+ String installerPackageName =
+ pm.getInstallerPackageName(info.getPackageName());
+ if (installerPackageName != null) {
+ params.setInstallerPackageName(installerPackageName);
+ }
+ }
+ params.setRequestDowngrade(true);
+ params.setRequiredInstalledVersionCode(
+ info.getVersionRolledBackFrom().getLongVersionCode());
+ if (rollback.isStaged()) {
+ params.setStaged();
+ }
+ if (info.isApex()) {
+ params.setInstallAsApex();
+ }
+ int sessionId = packageInstaller.createSession(params);
+ PackageInstaller.Session session = packageInstaller.openSession(sessionId);
+ File[] packageCodePaths = RollbackStore.getPackageCodePaths(
+ rollback, info.getPackageName());
+ if (packageCodePaths == null) {
+ sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE,
+ "Backup copy of package inaccessible");
+ return;
+ }
+
+ for (File packageCodePath : packageCodePaths) {
+ try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(packageCodePath,
ParcelFileDescriptor.MODE_READ_ONLY)) {
- final long token = Binder.clearCallingIdentity();
- try {
- session.write(packageCodePath.getName(), 0, packageCodePath.length(),
- fd);
- } finally {
- Binder.restoreCallingIdentity(token);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ session.write(packageCodePath.getName(), 0,
+ packageCodePath.length(),
+ fd);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
}
+ parentSession.addChildSessionId(sessionId);
}
- parentSession.addChildSessionId(sessionId);
- }
- final LocalIntentReceiver receiver = new LocalIntentReceiver(
- (Intent result) -> {
- getHandler().post(() -> {
+ final LocalIntentReceiver receiver = new LocalIntentReceiver(
+ (Intent result) -> getHandler().post(() -> {
int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
PackageInstaller.STATUS_FAILURE);
@@ -450,21 +465,22 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
// TODO: Should we just kill this rollback if
// commit failed? Why would we expect commit
// not to fail again?
- synchronized (mLock) {
- // TODO: Could this cause a rollback to be
- // resurrected if it should otherwise have
- // expired by now?
+ // TODO: Could this cause a rollback to be
+ // resurrected if it should otherwise have
+ // expired by now?
+ synchronized (rollback.getLock()) {
rollback.setAvailable();
rollback.setRestoreUserDataInProgress(false);
}
- sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE_INSTALL,
+ sendFailure(statusReceiver,
+ RollbackManager.STATUS_FAILURE_INSTALL,
"Rollback downgrade install failed: "
- + result.getStringExtra(
+ + result.getStringExtra(
PackageInstaller.EXTRA_STATUS_MESSAGE));
return;
}
- synchronized (mLock) {
+ synchronized (rollback.getLock()) {
if (!rollback.isStaged()) {
// All calls to restoreUserData should have
// completed by now for a non-staged install.
@@ -473,32 +489,31 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
rollback.info.setCommittedSessionId(parentSessionId);
rollback.info.getCausePackages().addAll(causePackages);
+ RollbackStore.deletePackageCodePaths(rollback);
+ saveRollback(rollback);
}
- mRollbackStore.deletePackageCodePaths(rollback);
- saveRollback(rollback);
sendSuccess(statusReceiver);
Intent broadcast = new Intent(Intent.ACTION_ROLLBACK_COMMITTED);
for (UserInfo userInfo : UserManager.get(mContext).getUsers(true)) {
- mContext.sendBroadcastAsUser(broadcast, userInfo.getUserHandle(),
+ mContext.sendBroadcastAsUser(broadcast,
+ userInfo.getUserHandle(),
Manifest.permission.MANAGE_ROLLBACKS);
}
- });
- }
- );
+ })
+ );
- synchronized (mLock) {
rollback.setCommitted();
rollback.setRestoreUserDataInProgress(true);
+ parentSession.commit(receiver.getIntentSender());
+ } catch (IOException e) {
+ Slog.e(TAG, "Rollback failed", e);
+ sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE,
+ "IOException: " + e.toString());
+ return;
}
- parentSession.commit(receiver.getIntentSender());
- } catch (IOException e) {
- Slog.e(TAG, "Rollback failed", e);
- sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE,
- "IOException: " + e.toString());
- return;
}
}
@@ -534,19 +549,17 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
Iterator<Rollback> iter = mRollbacks.iterator();
while (iter.hasNext()) {
Rollback rollback = iter.next();
- for (PackageRollbackInfo info : rollback.info.getPackages()) {
- if (info.getPackageName().equals(packageName)) {
+ synchronized (rollback.getLock()) {
+ if (rollback.includesPackage(packageName)) {
iter.remove();
deleteRollback(rollback);
- break;
}
}
}
for (NewRollback newRollback : mNewRollbacks) {
- for (PackageRollbackInfo info : newRollback.rollback.info.getPackages()) {
- if (info.getPackageName().equals(packageName)) {
+ synchronized (newRollback.rollback.getLock()) {
+ if (newRollback.rollback.includesPackage(packageName)) {
newRollback.isCancelled = true;
- break;
}
}
}
@@ -578,12 +591,16 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
rollbacks = new ArrayList<>(mRollbacks);
}
- final Set<Rollback> changed =
- mAppDataRollbackHelper.commitPendingBackupAndRestoreForUser(userId, rollbacks);
-
- for (Rollback rollback : changed) {
- saveRollback(rollback);
+ for (int i = 0; i < rollbacks.size(); i++) {
+ Rollback rollback = rollbacks.get(i);
+ synchronized (rollback.getLock()) {
+ if (mAppDataRollbackHelper.commitPendingBackupAndRestoreForUser(
+ userId, rollback)) {
+ saveRollback(rollback);
+ }
+ }
}
+
latch.countDown();
});
@@ -617,17 +634,15 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
Set<String> apexPackageNames = new HashSet<>();
synchronized (mLock) {
for (Rollback rollback : mRollbacks) {
- if (rollback.isStaged()) {
- if (rollback.isEnabling()) {
- enabling.add(rollback);
- } else if (rollback.isRestoreUserDataInProgress()) {
- restoreInProgress.add(rollback);
- }
-
- for (PackageRollbackInfo info : rollback.info.getPackages()) {
- if (info.isApex()) {
- apexPackageNames.add(info.getPackageName());
+ synchronized (rollback.getLock()) {
+ if (rollback.isStaged()) {
+ if (rollback.isEnabling()) {
+ enabling.add(rollback);
+ } else if (rollback.isRestoreUserDataInProgress()) {
+ restoreInProgress.add(rollback);
}
+
+ apexPackageNames.addAll(rollback.getApexPackageNames());
}
}
}
@@ -635,30 +650,32 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
for (Rollback rollback : enabling) {
PackageInstaller installer = mContext.getPackageManager().getPackageInstaller();
- 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
- // unavailable until the next reboot when it will go
- // away on its own?
- deleteRollback(rollback);
- } else if (session.isStagedSessionApplied()) {
- makeRollbackAvailable(rollback);
+ synchronized (rollback.getLock()) {
+ 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
+ // unavailable until the next reboot when it will go
+ // away on its own?
+ deleteRollback(rollback);
+ } else if (session.isStagedSessionApplied()) {
+ makeRollbackAvailable(rollback);
+ }
}
}
for (Rollback rollback : restoreInProgress) {
PackageInstaller installer = mContext.getPackageManager().getPackageInstaller();
- PackageInstaller.SessionInfo session =
- installer.getSessionInfo(rollback.getStagedSessionId());
- // TODO: What if session is null?
- if (session != null) {
- if (session.isStagedSessionApplied() || session.isStagedSessionFailed()) {
- synchronized (mLock) {
+ synchronized (rollback.getLock()) {
+ PackageInstaller.SessionInfo session =
+ installer.getSessionInfo(rollback.getStagedSessionId());
+ // TODO: What if session is null?
+ if (session != null) {
+ if (session.isStagedSessionApplied() || session.isStagedSessionFailed()) {
rollback.setRestoreUserDataInProgress(false);
+ saveRollback(rollback);
}
- saveRollback(rollback);
}
}
}
@@ -687,23 +704,19 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
private void onPackageReplaced(String packageName) {
// TODO: Could this end up incorrectly deleting a rollback for a
// package that is about to be installed?
- VersionedPackage installedVersion = getInstalledPackageVersion(packageName);
+ long installedVersion = getInstalledPackageVersion(packageName);
synchronized (mLock) {
Iterator<Rollback> iter = mRollbacks.iterator();
while (iter.hasNext()) {
Rollback rollback = iter.next();
- // TODO: Should we remove rollbacks in the ENABLING state here?
- if (rollback.isEnabling() || rollback.isAvailable()) {
- for (PackageRollbackInfo info : rollback.info.getPackages()) {
- if (info.getPackageName().equals(packageName)
- && !packageVersionsEqual(
- info.getVersionRolledBackFrom(),
- installedVersion)) {
- iter.remove();
- deleteRollback(rollback);
- break;
- }
+ synchronized (rollback.getLock()) {
+ // TODO: Should we remove rollbacks in the ENABLING state here?
+ if ((rollback.isEnabling() || rollback.isAvailable())
+ && rollback.includesPackageWithDifferentVersion(packageName,
+ installedVersion)) {
+ iter.remove();
+ deleteRollback(rollback);
}
}
}
@@ -760,16 +773,18 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
Iterator<Rollback> iter = mRollbacks.iterator();
while (iter.hasNext()) {
Rollback rollback = iter.next();
- if (!rollback.isAvailable()) {
- continue;
- }
- if (!now.isBefore(
+ synchronized (rollback.getLock()) {
+ if (!rollback.isAvailable()) {
+ continue;
+ }
+ if (!now.isBefore(
rollback.getTimestamp()
.plusMillis(mRollbackLifetimeDurationInMillis))) {
- iter.remove();
- deleteRollback(rollback);
- } else if (oldest == null || oldest.isAfter(rollback.getTimestamp())) {
- oldest = rollback.getTimestamp();
+ iter.remove();
+ deleteRollback(rollback);
+ } else if (oldest == null || oldest.isAfter(rollback.getTimestamp())) {
+ oldest = rollback.getTimestamp();
+ }
}
}
}
@@ -877,10 +892,12 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
synchronized (mLock) {
for (int i = 0; i < mRollbacks.size(); ++i) {
Rollback rollback = mRollbacks.get(i);
- 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;
+ synchronized (rollback.getLock()) {
+ 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;
+ }
}
}
}
@@ -979,6 +996,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
new IntArray() /* pendingBackups */, new ArrayList<>() /* pendingRestores */,
isApex, new IntArray(), new SparseLongArray() /* ceSnapshotInodes */);
+
try {
ApplicationInfo appInfo = pkgInfo.applicationInfo;
RollbackStore.backupPackageCodePath(rollback, packageName, appInfo.sourceDir);
@@ -992,7 +1010,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
return false;
}
- synchronized (mLock) {
+ synchronized (rollback.getLock()) {
rollback.info.getPackages().add(packageRollbackInfo);
}
return true;
@@ -1020,27 +1038,31 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
// staged installs
for (int i = 0; i < mRollbacks.size(); i++) {
Rollback rollback = mRollbacks.get(i);
- if (!rollback.isEnabling()) {
- continue;
- }
+ synchronized (rollback.getLock()) {
+ if (!rollback.isEnabling()) {
+ continue;
+ }
- for (PackageRollbackInfo info : rollback.info.getPackages()) {
- if (info.getPackageName().equals(packageName)) {
- mAppDataRollbackHelper.snapshotAppData(
- rollback.info.getRollbackId(), info, userIds);
- saveRollback(rollback);
- break;
+ for (PackageRollbackInfo info : rollback.info.getPackages()) {
+ if (info.getPackageName().equals(packageName)) {
+ mAppDataRollbackHelper.snapshotAppData(
+ rollback.info.getRollbackId(), info, userIds);
+ saveRollback(rollback);
+ break;
+ }
}
}
}
// non-staged installs
PackageRollbackInfo info;
for (NewRollback rollback : mNewRollbacks) {
- info = getPackageRollbackInfo(rollback.rollback, packageName);
- if (info != null) {
- mAppDataRollbackHelper.snapshotAppData(
- rollback.rollback.info.getRollbackId(), info, userIds);
- saveRollback(rollback.rollback);
+ synchronized (rollback.rollback.getLock()) {
+ info = getPackageRollbackInfo(rollback.rollback, packageName);
+ if (info != null) {
+ mAppDataRollbackHelper.snapshotAppData(
+ rollback.rollback.info.getRollbackId(), info, userIds);
+ saveRollback(rollback.rollback);
+ }
}
}
}
@@ -1053,11 +1075,13 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
synchronized (mLock) {
for (int i = 0; i < mRollbacks.size(); ++i) {
Rollback candidate = mRollbacks.get(i);
- if (candidate.isRestoreUserDataInProgress()) {
- info = getPackageRollbackInfo(candidate, packageName);
- if (info != null) {
- rollback = candidate;
- break;
+ synchronized (candidate.getLock()) {
+ if (candidate.isRestoreUserDataInProgress()) {
+ info = getPackageRollbackInfo(candidate, packageName);
+ if (info != null) {
+ rollback = candidate;
+ break;
+ }
}
}
}
@@ -1068,12 +1092,14 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
}
for (int userId : userIds) {
- final boolean changedRollback = mAppDataRollbackHelper.restoreAppData(
- rollback.info.getRollbackId(), info, userId, appId, seInfo);
+ synchronized (rollback.getLock()) {
+ final boolean changedRollback = mAppDataRollbackHelper.restoreAppData(
+ rollback.info.getRollbackId(), info, userId, appId, seInfo);
- // We've updated metadata about this rollback, so save it to flash.
- if (changedRollback) {
- saveRollback(rollback);
+ // We've updated metadata about this rollback, so save it to flash.
+ if (changedRollback) {
+ saveRollback(rollback);
+ }
}
}
}
@@ -1147,7 +1173,6 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
for (int i = 0; i < mRollbacks.size(); ++i) {
Rollback candidate = mRollbacks.get(i);
if (candidate.getStagedSessionId() == originalSessionId) {
- candidate.setApkSessionId(apkSessionId);
rollback = candidate;
break;
}
@@ -1162,7 +1187,10 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
}
if (rollback != null) {
- saveRollback(rollback);
+ synchronized (rollback.getLock()) {
+ rollback.setApkSessionId(apkSessionId);
+ saveRollback(rollback);
+ }
}
});
}
@@ -1207,18 +1235,18 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
/**
* Gets the version of the package currently installed.
- * Returns null if the package is not currently installed.
+ * Returns -1 if the package is not currently installed.
*/
- private VersionedPackage getInstalledPackageVersion(String packageName) {
+ private long getInstalledPackageVersion(String packageName) {
PackageManager pm = mContext.getPackageManager();
PackageInfo pkgInfo = null;
try {
pkgInfo = getPackageInfo(packageName);
} catch (PackageManager.NameNotFoundException e) {
- return null;
+ return -1;
}
- return new VersionedPackage(packageName, pkgInfo.getLongVersionCode());
+ return pkgInfo.getLongVersionCode();
}
/**
@@ -1273,44 +1301,49 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
if (newRollback != null) {
Rollback rollback = completeEnableRollback(newRollback, success);
- if (rollback != null && !rollback.isStaged()) {
- makeRollbackAvailable(rollback);
+ if (rollback != null) {
+ synchronized (rollback.getLock()) {
+ if (!rollback.isStaged()) {
+ makeRollbackAvailable(rollback);
+ }
+ }
}
}
}
}
/**
- * Add a rollback to the list of rollbacks.
- * This should be called after rollback has been enabled for all packages
- * in the rollback. It does not make the rollback available yet.
+ * Add a rollback to the list of rollbacks. This should be called after rollback has been
+ * enabled for all packages in the rollback. It does not make the rollback available yet.
+ *
+ * <p>Note that no rollback-specific locks should be held when this method is called.
*
* @return the Rollback instance for a successfully enable-completed rollback,
* or null on error.
*/
private Rollback completeEnableRollback(NewRollback newRollback, boolean success) {
Rollback rollback = newRollback.rollback;
- if (!success) {
- // The install session was aborted, clean up the pending install.
- deleteRollback(rollback);
- return null;
- }
- if (newRollback.isCancelled) {
- Slog.e(TAG, "Rollback has been cancelled by PackageManager");
- deleteRollback(rollback);
- return null;
- }
+ synchronized (rollback.getLock()) {
+ if (!success) {
+ // The install session was aborted, clean up the pending install.
+ deleteRollback(rollback);
+ return null;
+ }
+ if (newRollback.isCancelled) {
+ Slog.e(TAG, "Rollback has been cancelled by PackageManager");
+ deleteRollback(rollback);
+ return null;
+ }
- // It's safe to access rollback.info outside a synchronized block because
- // this is running on the handler thread and all changes to the
- // rollback.info occur on the handler thread.
- if (rollback.info.getPackages().size() != newRollback.packageSessionIds.length) {
- Slog.e(TAG, "Failed to enable rollback for all packages in session.");
- deleteRollback(rollback);
- return null;
- }
- saveRollback(rollback);
+ if (rollback.info.getPackages().size() != newRollback.packageSessionIds.length) {
+ Slog.e(TAG, "Failed to enable rollback for all packages in session.");
+ deleteRollback(rollback);
+ return null;
+ }
+
+ saveRollback(rollback);
+ }
synchronized (mLock) {
// Note: There is a small window of time between when
// the session has been committed by the package
@@ -1328,14 +1361,13 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
return rollback;
}
+ @GuardedBy("rollback.getLock")
private void makeRollbackAvailable(Rollback rollback) {
// TODO: What if the rollback has since been expired, for example due
// 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.setAvailable();
- rollback.setTimestamp(Instant.now());
- }
+ rollback.setAvailable();
+ rollback.setTimestamp(Instant.now());
saveRollback(rollback);
// TODO(zezeozue): Provide API to explicitly start observing instead
@@ -1343,11 +1375,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
// should document in PackageInstaller.SessionParams#setEnableRollback
// After enabling and commiting any rollback, observe packages and
// prepare to rollback if packages crashes too frequently.
- List<String> packages = new ArrayList<>();
- for (int i = 0; i < rollback.info.getPackages().size(); i++) {
- packages.add(rollback.info.getPackages().get(i).getPackageName());
- }
- mPackageHealthObserver.startObservingHealth(packages,
+ mPackageHealthObserver.startObservingHealth(rollback.getPackageNames(),
mRollbackLifetimeDurationInMillis);
scheduleExpiration(mRollbackLifetimeDurationInMillis);
}
@@ -1372,6 +1400,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
* Returns the {@code PackageRollbackInfo} associated with {@code packageName} from
* a specified {@code Rollback}.
*/
+ @GuardedBy("rollback.getLock")
private static PackageRollbackInfo getPackageRollbackInfo(Rollback rollback,
String packageName) {
for (PackageRollbackInfo info : rollback.info.getPackages()) {
@@ -1398,6 +1427,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
throw new IllegalStateException("Failed to allocate rollback ID");
}
+ @GuardedBy("rollback.getLock")
private void deleteRollback(Rollback rollback) {
for (PackageRollbackInfo info : rollback.info.getPackages()) {
IntArray snapshottedUsers = info.getSnapshottedUsers();
@@ -1416,6 +1446,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
* TODO: Double check we can't do a better job handling the IOException in
* a cases where this method is called.
*/
+ @GuardedBy("rollback.getLock")
private void saveRollback(Rollback rollback) {
try {
mRollbackStore.saveRollback(rollback);
@@ -1430,32 +1461,34 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
synchronized (mLock) {
for (Rollback rollback : mRollbacks) {
- RollbackInfo info = rollback.info;
- ipw.println(info.getRollbackId() + ":");
- ipw.increaseIndent();
- ipw.println("-state: " + rollback.getStateAsString());
- ipw.println("-timestamp: " + rollback.getTimestamp());
- if (rollback.getStagedSessionId() != -1) {
- ipw.println("-stagedSessionId: " + rollback.getStagedSessionId());
- }
- ipw.println("-packages:");
- ipw.increaseIndent();
- for (PackageRollbackInfo pkg : info.getPackages()) {
- ipw.println(pkg.getPackageName()
- + " " + pkg.getVersionRolledBackFrom().getLongVersionCode()
- + " -> " + pkg.getVersionRolledBackTo().getLongVersionCode());
- }
- ipw.decreaseIndent();
- if (rollback.isCommitted()) {
- ipw.println("-causePackages:");
+ synchronized (rollback.getLock()) {
+ RollbackInfo info = rollback.info;
+ ipw.println(info.getRollbackId() + ":");
+ ipw.increaseIndent();
+ ipw.println("-state: " + rollback.getStateAsString());
+ ipw.println("-timestamp: " + rollback.getTimestamp());
+ if (rollback.getStagedSessionId() != -1) {
+ ipw.println("-stagedSessionId: " + rollback.getStagedSessionId());
+ }
+ ipw.println("-packages:");
ipw.increaseIndent();
- for (VersionedPackage cPkg : info.getCausePackages()) {
- ipw.println(cPkg.getPackageName() + " " + cPkg.getLongVersionCode());
+ for (PackageRollbackInfo pkg : info.getPackages()) {
+ ipw.println(pkg.getPackageName()
+ + " " + pkg.getVersionRolledBackFrom().getLongVersionCode()
+ + " -> " + pkg.getVersionRolledBackTo().getLongVersionCode());
+ }
+ ipw.decreaseIndent();
+ if (rollback.isCommitted()) {
+ ipw.println("-causePackages:");
+ ipw.increaseIndent();
+ for (VersionedPackage cPkg : info.getCausePackages()) {
+ ipw.println(cPkg.getPackageName() + " " + cPkg.getLongVersionCode());
+ }
+ ipw.decreaseIndent();
+ ipw.println("-committedSessionId: " + info.getCommittedSessionId());
}
ipw.decreaseIndent();
- ipw.println("-committedSessionId: " + info.getCommittedSessionId());
}
- ipw.decreaseIndent();
}
}
}
@@ -1516,7 +1549,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
}
}
- NewRollback createNewRollbackLocked(PackageInstaller.SessionInfo parentSession) {
+ @GuardedBy("mLock")
+ private NewRollback createNewRollbackLocked(PackageInstaller.SessionInfo parentSession) {
int rollbackId = allocateRollbackIdLocked();
final Rollback rollback;
int parentSessionId = parentSession.getSessionId();
diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java
index 772c53fec4ce..b6d1f1875907 100644
--- a/services/core/java/com/android/server/rollback/RollbackStore.java
+++ b/services/core/java/com/android/server/rollback/RollbackStore.java
@@ -27,6 +27,8 @@ import android.util.IntArray;
import android.util.Slog;
import android.util.SparseLongArray;
+import com.android.internal.annotations.GuardedBy;
+
import libcore.io.IoUtils;
import org.json.JSONArray;
@@ -250,6 +252,7 @@ class RollbackStore {
/**
* Saves the given rollback to persistent storage.
*/
+ @GuardedBy("rollback.getLock")
void saveRollback(Rollback rollback) throws IOException {
try {
JSONObject dataJson = new JSONObject();
diff --git a/services/core/java/com/android/server/security/VerityUtils.java b/services/core/java/com/android/server/security/VerityUtils.java
index b1db46fb3276..856a40f3ef12 100644
--- a/services/core/java/com/android/server/security/VerityUtils.java
+++ b/services/core/java/com/android/server/security/VerityUtils.java
@@ -26,27 +26,19 @@ import android.util.Slog;
import android.util.apk.ApkSignatureVerifier;
import android.util.apk.ByteBufferFactory;
import android.util.apk.SignatureNotFoundException;
-import android.util.apk.VerityBuilder;
import libcore.util.HexEncoding;
import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
-import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.channels.FileChannel;
import java.nio.file.Files;
-import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.DigestException;
-import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
-import sun.security.pkcs.PKCS7;
-
/** Provides fsverity related operations. */
abstract public class VerityUtils {
private static final String TAG = "VerityUtils";
@@ -60,8 +52,6 @@ abstract public class VerityUtils {
/** The maximum size of signature file. This is just to avoid potential abuse. */
private static final int MAX_SIGNATURE_FILE_SIZE_BYTES = 8192;
- private static final int COMMON_LINUX_PAGE_SIZE_IN_BYTES = 4096;
-
private static final boolean DEBUG = false;
/** Returns true if the given file looks like containing an fs-verity signature. */
@@ -74,42 +64,15 @@ abstract public class VerityUtils {
return filePath + FSVERITY_SIGNATURE_FILE_EXTENSION;
}
- /** Generates Merkle tree and fs-verity metadata then enables fs-verity. */
- public static void setUpFsverity(@NonNull String filePath, String signaturePath)
- throws IOException, DigestException, NoSuchAlgorithmException {
- final PKCS7 pkcs7 = new PKCS7(Files.readAllBytes(Paths.get(signaturePath)));
- final byte[] expectedMeasurement = pkcs7.getContentInfo().getContentBytes();
- if (DEBUG) {
- Slog.d(TAG, "Enabling fs-verity with signed fs-verity measurement "
- + bytesToString(expectedMeasurement));
- Slog.d(TAG, "PKCS#7 info: " + pkcs7);
- }
-
- final TrackedBufferFactory bufferFactory = new TrackedBufferFactory();
- final byte[] actualMeasurement = generateFsverityMetadata(filePath, signaturePath,
- bufferFactory);
- try (RandomAccessFile raf = new RandomAccessFile(filePath, "rw")) {
- FileChannel ch = raf.getChannel();
- ch.position(roundUpToNextMultiple(ch.size(), COMMON_LINUX_PAGE_SIZE_IN_BYTES));
- ByteBuffer buffer = bufferFactory.getBuffer();
-
- long offset = buffer.position();
- long size = buffer.limit();
- while (offset < size) {
- long s = ch.write(buffer);
- offset += s;
- size -= s;
- }
+ /** Enables fs-verity for the file with a PKCS#7 detached signature file. */
+ public static void setUpFsverity(@NonNull String filePath, @NonNull String signaturePath)
+ throws IOException {
+ if (Files.size(Paths.get(signaturePath)) > MAX_SIGNATURE_FILE_SIZE_BYTES) {
+ throw new SecurityException("Signature file is unexpectedly large: " + signaturePath);
}
-
- if (!Arrays.equals(expectedMeasurement, actualMeasurement)) {
- throw new SecurityException("fs-verity measurement mismatch: "
- + bytesToString(actualMeasurement) + " != "
- + bytesToString(expectedMeasurement));
- }
-
- // This can fail if the public key is not already in .fs-verity kernel keyring.
- int errno = enableFsverityNative(filePath);
+ byte[] pkcs7Signature = Files.readAllBytes(Paths.get(signaturePath));
+ // This will fail if the public key is not already in .fs-verity kernel keyring.
+ int errno = enableFsverityNative(filePath, pkcs7Signature);
if (errno != 0) {
throw new IOException("Failed to enable fs-verity on " + filePath + ": "
+ Os.strerror(errno));
@@ -131,12 +94,19 @@ abstract public class VerityUtils {
return true;
}
+ private static native int enableFsverityNative(@NonNull String filePath,
+ @NonNull byte[] pkcs7Signature);
+ private static native int measureFsverityNative(@NonNull String filePath);
+
/**
* Generates legacy Merkle tree and fs-verity metadata with Signing Block skipped.
*
+ * @deprecated This is only used for previous fs-verity implementation, and should never be used
+ * on new devices.
* @return {@code SetupResult} that contains the result code, and when success, the
* {@code FileDescriptor} to read all the data from.
*/
+ @Deprecated
public static SetupResult generateApkVeritySetupData(@NonNull String apkPath) {
if (DEBUG) {
Slog.d(TAG, "Trying to install legacy apk verity to " + apkPath);
@@ -173,7 +143,10 @@ abstract public class VerityUtils {
/**
* {@see ApkSignatureVerifier#generateApkVerityRootHash(String)}.
+ * @deprecated This is only used for previous fs-verity implementation, and should never be used
+ * on new devices.
*/
+ @Deprecated
public static byte[] generateApkVerityRootHash(@NonNull String apkPath)
throws NoSuchAlgorithmException, DigestException, IOException {
return ApkSignatureVerifier.generateApkVerityRootHash(apkPath);
@@ -181,104 +154,16 @@ abstract public class VerityUtils {
/**
* {@see ApkSignatureVerifier#getVerityRootHash(String)}.
+ * @deprecated This is only used for previous fs-verity implementation, and should never be used
+ * on new devices.
*/
+ @Deprecated
public static byte[] getVerityRootHash(@NonNull String apkPath)
throws IOException, SignatureNotFoundException {
return ApkSignatureVerifier.getVerityRootHash(apkPath);
}
/**
- * Generates fs-verity metadata for {@code filePath} in the buffer created by {@code
- * trackedBufferFactory}. The metadata contains the Merkle tree, fs-verity descriptor and
- * extensions, including a PKCS#7 signature provided in {@code signaturePath}.
- *
- * <p>It is worthy to note that {@code trackedBufferFactory} generates a "tracked" {@code
- * ByteBuffer}. The data will be used outside this method via the factory itself.
- *
- * @return fs-verity signed data (struct fsverity_digest_disk) of {@code filePath}, which
- * includes SHA-256 of fs-verity descriptor and authenticated extensions.
- */
- private static byte[] generateFsverityMetadata(String filePath, String signaturePath,
- @NonNull ByteBufferFactory trackedBufferFactory)
- throws IOException, DigestException, NoSuchAlgorithmException {
- try (RandomAccessFile file = new RandomAccessFile(filePath, "r")) {
- VerityBuilder.VerityResult result = VerityBuilder.generateFsVerityTree(
- file, trackedBufferFactory);
-
- ByteBuffer buffer = result.verityData;
- buffer.position(result.merkleTreeSize);
-
- final byte[] measurement = generateFsverityDescriptorAndMeasurement(file,
- result.rootHash, signaturePath, buffer);
- buffer.flip();
- return constructFsveritySignedDataNative(measurement);
- }
- }
-
- /**
- * Generates fs-verity descriptor including the extensions to the {@code output} and returns the
- * fs-verity measurement.
- *
- * @return fs-verity measurement, which is a SHA-256 of fs-verity descriptor and authenticated
- * extensions.
- */
- private static byte[] generateFsverityDescriptorAndMeasurement(
- @NonNull RandomAccessFile file, @NonNull byte[] rootHash,
- @NonNull String pkcs7SignaturePath, @NonNull ByteBuffer output)
- throws IOException, NoSuchAlgorithmException, DigestException {
- final short kRootHashExtensionId = 1;
- final short kPkcs7SignatureExtensionId = 3;
- final int origPosition = output.position();
-
- // For generating fs-verity file measurement, which consists of the descriptor and
- // authenticated extensions (but not unauthenticated extensions and the footer).
- MessageDigest md = MessageDigest.getInstance("SHA-256");
-
- // 1. Generate fs-verity descriptor.
- final byte[] desc = constructFsverityDescriptorNative(file.length());
- output.put(desc);
- md.update(desc);
-
- // 2. Generate authenticated extensions.
- final byte[] authExt =
- constructFsverityExtensionNative(kRootHashExtensionId, rootHash.length);
- output.put(authExt);
- output.put(rootHash);
- md.update(authExt);
- md.update(rootHash);
-
- // 3. Generate unauthenticated extensions.
- ByteBuffer header = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN);
- output.putShort((short) 1); // number of unauthenticated extensions below
- output.position(output.position() + 6);
-
- // Generate PKCS#7 extension. NB: We do not verify agaist trusted certificate (should be
- // done by the caller if needed).
- Path path = Paths.get(pkcs7SignaturePath);
- if (Files.size(path) > MAX_SIGNATURE_FILE_SIZE_BYTES) {
- throw new IllegalArgumentException("Signature size is unexpectedly large: "
- + pkcs7SignaturePath);
- }
- final byte[] pkcs7Signature = Files.readAllBytes(path);
- output.put(constructFsverityExtensionNative(kPkcs7SignatureExtensionId,
- pkcs7Signature.length));
- output.put(pkcs7Signature);
-
- // 4. Generate the footer.
- output.put(constructFsverityFooterNative(output.position() - origPosition));
-
- return md.digest();
- }
-
- private static native int enableFsverityNative(@NonNull String filePath);
- private static native int measureFsverityNative(@NonNull String filePath);
- private static native byte[] constructFsveritySignedDataNative(@NonNull byte[] measurement);
- private static native byte[] constructFsverityDescriptorNative(long fileSize);
- private static native byte[] constructFsverityExtensionNative(short extensionId,
- int extensionDataSize);
- private static native byte[] constructFsverityFooterNative(int offsetToDescriptorHead);
-
- /**
* Returns a pair of {@code SharedMemory} and {@code Integer}. The {@code SharedMemory} contains
* Merkle tree and fsverity headers for the given apk, in the form that can immediately be used
* for fsverity setup. The data is aligned to the beginning of {@code SharedMemory}, and has
@@ -313,6 +198,11 @@ abstract public class VerityUtils {
return HexEncoding.encodeToString(bytes);
}
+ /**
+ * @deprecated This is only used for previous fs-verity implementation, and should never be used
+ * on new devices.
+ */
+ @Deprecated
public static class SetupResult {
/** Result code if verity is set up correctly. */
private static final int RESULT_OK = 1;
@@ -401,30 +291,4 @@ abstract public class VerityUtils {
return mBuffer == null ? -1 : mBuffer.limit();
}
}
-
- /** A {@code ByteBufferFactory} that tracks the {@code ByteBuffer} it creates. */
- private static class TrackedBufferFactory implements ByteBufferFactory {
- private ByteBuffer mBuffer;
-
- @Override
- public ByteBuffer create(int capacity) {
- if (mBuffer != null) {
- throw new IllegalStateException("Multiple instantiation from this factory");
- }
- mBuffer = ByteBuffer.allocate(capacity);
- return mBuffer;
- }
-
- public ByteBuffer getBuffer() {
- return mBuffer;
- }
- }
-
- /** Round up the number to the next multiple of the divisor. */
- private static long roundUpToNextMultiple(long number, long divisor) {
- if (number > (Long.MAX_VALUE - divisor)) {
- throw new IllegalArgumentException("arithmetic overflow");
- }
- return ((number + (divisor - 1)) / divisor) * divisor;
- }
}
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/ProcfsMemoryUtil.java b/services/core/java/com/android/server/stats/ProcfsMemoryUtil.java
new file mode 100644
index 000000000000..d49b9589cb15
--- /dev/null
+++ b/services/core/java/com/android/server/stats/ProcfsMemoryUtil.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2017 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 com.android.internal.annotations.VisibleForTesting;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+final class ProcfsMemoryUtil {
+ private static final String TAG = "ProcfsMemoryUtil";
+
+ /** Path to procfs status file: /proc/pid/status. */
+ private static final String STATUS_FILE_FMT = "/proc/%d/status";
+
+ private static final Pattern RSS_HIGH_WATER_MARK_IN_KILOBYTES =
+ Pattern.compile("VmHWM:\\s*(\\d+)\\s*kB");
+ private static final Pattern RSS_IN_KILOBYTES =
+ Pattern.compile("VmRSS:\\s*(\\d+)\\s*kB");
+ private static final Pattern ANON_RSS_IN_KILOBYTES =
+ Pattern.compile("RssAnon:\\s*(\\d+)\\s*kB");
+ private static final Pattern SWAP_IN_KILOBYTES =
+ Pattern.compile("VmSwap:\\s*(\\d+)\\s*kB");
+
+ private ProcfsMemoryUtil() {}
+
+ /**
+ * Reads RSS high-water mark of a process from procfs. Returns value of the VmHWM field in
+ * /proc/PID/status in kilobytes or 0 if not available.
+ */
+ static int readRssHighWaterMarkFromProcfs(int pid) {
+ final String statusPath = String.format(Locale.US, STATUS_FILE_FMT, pid);
+ return parseVmHWMFromStatus(readFile(statusPath));
+ }
+
+ /**
+ * 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 int parseVmHWMFromStatus(String contents) {
+ return tryParseInt(contents, RSS_HIGH_WATER_MARK_IN_KILOBYTES);
+ }
+
+ /**
+ * Reads memory stat of a process from procfs. Returns values of the VmRss, AnonRSS, VmSwap
+ * fields in /proc/pid/status in kilobytes or 0 if not available.
+ */
+ static MemorySnapshot readMemorySnapshotFromProcfs(int pid) {
+ final String statusPath = String.format(Locale.US, STATUS_FILE_FMT, pid);
+ return parseMemorySnapshotFromStatus(readFile(statusPath));
+ }
+
+ @VisibleForTesting
+ static MemorySnapshot parseMemorySnapshotFromStatus(String contents) {
+ final MemorySnapshot snapshot = new MemorySnapshot();
+ snapshot.rssInKilobytes = tryParseInt(contents, RSS_IN_KILOBYTES);
+ snapshot.anonRssInKilobytes = tryParseInt(contents, ANON_RSS_IN_KILOBYTES);
+ snapshot.swapInKilobytes = tryParseInt(contents, SWAP_IN_KILOBYTES);
+ return snapshot;
+ }
+
+ private static String readFile(String path) {
+ try {
+ final File file = new File(path);
+ return FileUtils.readTextFile(file, 0 /* max */, null /* ellipsis */);
+ } catch (IOException e) {
+ return "";
+ }
+ }
+
+ private static int tryParseInt(String contents, Pattern pattern) {
+ if (contents.isEmpty()) {
+ return 0;
+ }
+ final Matcher matcher = pattern.matcher(contents);
+ try {
+ return matcher.find() ? Integer.parseInt(matcher.group(1)) : 0;
+ } catch (NumberFormatException e) {
+ Slog.e(TAG, "Failed to parse value", e);
+ return 0;
+ }
+ }
+
+ static final class MemorySnapshot {
+ public int rssInKilobytes;
+ public int anonRssInKilobytes;
+ public int swapInKilobytes;
+
+ boolean isEmpty() {
+ return (anonRssInKilobytes + swapInKilobytes) == 0;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index e176480f685d..e1a48ed3b550 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -27,9 +27,10 @@ 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 static com.android.server.stats.ProcfsMemoryUtil.readMemorySnapshotFromProcfs;
+import static com.android.server.stats.ProcfsMemoryUtil.readRssHighWaterMarkFromProcfs;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -138,9 +139,10 @@ 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.stats.ProcfsMemoryUtil.MemorySnapshot;
import com.android.server.storage.DiskStatsFileLogger;
import com.android.server.storage.DiskStatsLoggingService;
@@ -1112,13 +1114,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);
}
}
@@ -1271,6 +1272,47 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
SystemProperties.set("sys.rss_hwm_reset.on", "1");
}
+ private void pullProcessMemorySnapshot(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ List<ProcessMemoryState> managedProcessList =
+ LocalServices.getService(
+ ActivityManagerInternal.class).getMemoryStateForProcesses();
+ for (ProcessMemoryState managedProcess : managedProcessList) {
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(managedProcess.uid);
+ e.writeString(managedProcess.processName);
+ e.writeInt(managedProcess.pid);
+ e.writeInt(managedProcess.oomScore);
+ final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(managedProcess.pid);
+ if (snapshot.isEmpty()) {
+ continue;
+ }
+ e.writeInt(snapshot.rssInKilobytes);
+ e.writeInt(snapshot.anonRssInKilobytes);
+ e.writeInt(snapshot.swapInKilobytes);
+ e.writeInt(snapshot.anonRssInKilobytes + snapshot.swapInKilobytes);
+ pulledData.add(e);
+ }
+ int[] pids = getPidsForCommands(MEMORY_INTERESTING_NATIVE_PROCESSES);
+ for (int pid : pids) {
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(getUidForPid(pid));
+ e.writeString(readCmdlineFromProcfs(pid));
+ e.writeInt(pid);
+ e.writeInt(-1001); // Placeholder for native processes, OOM_SCORE_ADJ_MIN - 1.
+ final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(pid);
+ if (snapshot.isEmpty()) {
+ continue;
+ }
+ e.writeInt(snapshot.rssInKilobytes);
+ e.writeInt(snapshot.anonRssInKilobytes);
+ e.writeInt(snapshot.swapInKilobytes);
+ e.writeInt(snapshot.anonRssInKilobytes + snapshot.swapInKilobytes);
+ pulledData.add(e);
+ }
+ }
+
private void pullSystemIonHeapSize(
int tagId, long elapsedNanos, long wallClockNanos,
List<StatsLogEventWrapper> pulledData) {
@@ -2353,6 +2395,10 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
pullProcessMemoryHighWaterMark(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+ case StatsLog.PROCESS_MEMORY_SNAPSHOT: {
+ pullProcessMemorySnapshot(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
case StatsLog.SYSTEM_ION_HEAP_SIZE: {
pullSystemIonHeapSize(tagId, elapsedNanos, wallClockNanos, ret);
break;
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..59f051bc76a6 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -1106,14 +1106,10 @@ final class AccessibilityController {
private final Point mTempPoint = new Point();
- private final Rect mTempRect = new Rect();
-
private final Region mTempRegion = new Region();
private final Region mTempRegion1 = new Region();
- private final Context mContext;
-
private final WindowManagerService mService;
private final Handler mHandler;
@@ -1127,7 +1123,6 @@ final class AccessibilityController {
public WindowsForAccessibilityObserver(WindowManagerService windowManagerService,
int displayId,
WindowsForAccessibilityCallback callback) {
- mContext = windowManagerService.mContext;
mService = windowManagerService;
mCallback = callback;
mDisplayId = displayId;
@@ -1246,6 +1241,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 61bf2cea0b86..c54ccd4d6844 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();
}
}
@@ -2202,7 +2202,9 @@ final class ActivityRecord extends ConfigurationContainer {
stack.removeTimeoutsForActivity(this);
// Clean-up activities are no longer relaunching (e.g. app process died). Notify window
// manager so it can update its bookkeeping.
- mAtmService.mWindowManager.notifyAppRelaunchesCleared(appToken);
+ if (mAppWindowToken != null) {
+ mAppWindowToken.clearRelaunching();
+ }
}
/**
@@ -2547,6 +2549,8 @@ final class ActivityRecord extends ConfigurationContainer {
return;
}
mAppWindowToken.setVisibility(visible, mDeferHidingClient);
+ mAtmService.addWindowLayoutReasons(
+ ActivityTaskManagerService.LAYOUT_REASON_VISIBILITY_CHANGED);
mStackSupervisor.getActivityMetricsLogger().notifyVisibilityChanged(this);
}
@@ -2959,6 +2963,11 @@ final class ActivityRecord extends ConfigurationContainer {
if (display != null) {
display.handleActivitySizeCompatModeIfNeeded(r);
}
+
+ if (r.mAppWindowToken != null) {
+ r.mAppWindowToken.getDisplayContent().mUnknownAppVisibilityController
+ .notifyAppResumedFinished(r.mAppWindowToken);
+ }
}
/**
@@ -4309,7 +4318,9 @@ final class ActivityRecord extends ConfigurationContainer {
"Moving to " + (andResume ? "RESUMED" : "PAUSED") + " Relaunching " + this
+ " callers=" + Debug.getCallers(6));
forceNewConfig = false;
- mStackSupervisor.activityRelaunchingLocked(this);
+ if (mAppWindowToken != null) {
+ mAppWindowToken.startRelaunching();
+ }
final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(pendingResults,
pendingNewIntents, configChangeFlags,
new MergedConfiguration(mAtmService.getGlobalConfiguration(),
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index a3896797b654..2ab3e01278e9 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);
@@ -840,17 +839,18 @@ class ActivityStack extends ConfigurationContainer {
// so that the divider matches and remove this logic.
// TODO: This is currently only called when entering split-screen while in another
// task, and from the tests
- // TODO (b/78247419): Check if launcher and overview are same then move home stack
- // instead of recents stack. Then fix the rotation animation from fullscreen to
- // minimized mode
+ // TODO (b/78247419): Fix the rotation animation from fullscreen to minimized mode
+ final boolean isRecentsComponentHome =
+ mService.getRecentTasks().isRecentsComponentHomeActivity(mCurrentUser);
final ActivityStack recentStack = display.getOrCreateStack(
- WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_RECENTS,
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
+ isRecentsComponentHome ? ACTIVITY_TYPE_HOME : ACTIVITY_TYPE_RECENTS,
true /* onTop */);
recentStack.moveToFront("setWindowingMode");
// If task moved to docked stack - show recents if needed.
mService.mWindowManager.showRecentApps();
}
- wm.continueSurfaceLayout();
+ mService.continueWindowLayout();
}
if (!deferEnsuringVisibility) {
@@ -1750,11 +1750,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 +4360,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 +4384,7 @@ class ActivityStack extends ConfigurationContainer {
topRunningActivityLocked(), preserveWindows);
}
} finally {
- mWindowManager.continueSurfaceLayout();
+ mService.continueWindowLayout();
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
}
@@ -4941,12 +4941,6 @@ class ActivityStack extends ConfigurationContainer {
}
}
-
- Rect getDefaultPictureInPictureBounds(float aspectRatio) {
- if (getTaskStack() == null) return null;
- return getTaskStack().getPictureInPictureBounds(aspectRatio, null /* currentStackBounds */);
- }
-
void animateResizePinnedStack(Rect sourceHintBounds, Rect toBounds, int animationDuration,
boolean fromFullscreen) {
if (!inPinnedWindowingMode()) return;
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 7a3f022d60bf..d151f86ff810 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);
}
}
@@ -2481,19 +2480,17 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
}
void activityRelaunchedLocked(IBinder token) {
- mWindowManager.notifyAppRelaunchingFinished(token);
final ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r != null) {
+ if (r.mAppWindowToken != null) {
+ r.mAppWindowToken.finishRelaunching();
+ }
if (r.getActivityStack().shouldSleepOrShutDownActivities()) {
r.setSleeping(true, true);
}
}
}
- void activityRelaunchingLocked(ActivityRecord r) {
- mWindowManager.notifyAppRelaunching(r.appToken);
- }
-
void logStackState() {
mActivityMetricsLogger.logWindowState();
}
@@ -2731,7 +2728,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 +2819,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..47be792802a0 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");
}
@@ -1545,10 +1550,11 @@ class ActivityStarter {
mService.mUgmInternal.grantUriPermissionFromIntent(mCallingUid, mStartActivity.packageName,
mIntent, mStartActivity.getUriPermissionsLocked(), mStartActivity.mUserId);
- mService.getPackageManagerInternalLocked().grantEphemeralAccess(
+ mService.getPackageManagerInternalLocked().grantImplicitAccess(
mStartActivity.mUserId, mIntent,
- UserHandle.getAppId(mStartActivity.info.applicationInfo.uid),
- UserHandle.getAppId(mCallingUid));
+ UserHandle.getAppId(mCallingUid),
+ UserHandle.getAppId(mStartActivity.info.applicationInfo.uid)
+ );
if (newTask) {
EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, mStartActivity.mUserId,
mStartActivity.getTaskRecord().taskId);
@@ -1654,9 +1660,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;
}
@@ -2337,7 +2342,12 @@ class ActivityStarter {
REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
"reparentingHome");
mMovedToFront = true;
+ } else if (launchStack.topTask() == null) {
+ // The task does not need to be reparented to the launch stack. Remove the
+ // launch stack if there is no activity in it.
+ launchStack.remove();
}
+
mOptions = null;
// We are moving a task to the front, use starting window to hide initial drawn
@@ -2538,7 +2548,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 +2667,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 +2695,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 ed7e12c00515..468a13d45d55 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -47,7 +47,6 @@ import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_
import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.content.res.Configuration.UI_MODE_TYPE_TELEVISION;
import static android.os.FactoryTest.FACTORY_TEST_HIGH_LEVEL;
import static android.os.FactoryTest.FACTORY_TEST_LOW_LEVEL;
import static android.os.FactoryTest.FACTORY_TEST_OFF;
@@ -124,6 +123,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;
@@ -549,7 +549,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
/** The dimensions of the thumbnails in the Recents UI. */
private int mThumbnailWidth;
private int mThumbnailHeight;
- private float mFullscreenThumbnailScale;
/**
* Flag that indicates if multi-window is enabled.
@@ -598,6 +597,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;
@@ -776,15 +788,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
com.android.internal.R.dimen.thumbnail_width);
mThumbnailHeight = res.getDimensionPixelSize(
com.android.internal.R.dimen.thumbnail_height);
-
- if ((globalConfig.uiMode & UI_MODE_TYPE_TELEVISION) == UI_MODE_TYPE_TELEVISION) {
- mFullscreenThumbnailScale = (float) res
- .getInteger(com.android.internal.R.integer.thumbnail_width_tv) /
- (float) globalConfig.screenWidthDp;
- } else {
- mFullscreenThumbnailScale = res.getFraction(
- com.android.internal.R.fraction.thumbnail_fullscreen_scale, 1, 1);
- }
}
}
@@ -1683,7 +1686,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
final long origId = Binder.clearCallingIdentity();
synchronized (mGlobalLock) {
ActivityRecord.activityResumedLocked(token);
- mWindowManager.notifyAppResumedFinished(token);
}
Binder.restoreCallingIdentity(origId);
}
@@ -2906,7 +2908,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
mAmInternal.enforceCallingPermission(Manifest.permission.UPDATE_LOCK_TASK_PACKAGES,
"updateLockTaskPackages()");
}
- synchronized (this) {
+ synchronized (mGlobalLock) {
if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Whitelisting " + userId + ":"
+ Arrays.toString(packages));
getLockTaskController().updateLockTaskPackages(userId, packages);
@@ -4388,17 +4390,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 {
@@ -5099,9 +5103,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,
@@ -5110,9 +5112,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
kept = ensureConfigAndVisibilityAfterUpdate(starting, changes);
} finally {
- if (mWindowManager != null) {
- mWindowManager.continueSurfaceLayout();
- }
+ continueWindowLayout();
}
if (result != null) {
@@ -5240,6 +5240,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);
}
@@ -5325,7 +5353,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
final float scaleFactor = Settings.System.getFloatForUser(mContext.getContentResolver(),
FONT_SCALE, 1.0f, userId);
- synchronized (this) {
+ synchronized (mGlobalLock) {
if (getGlobalConfiguration().fontScale == scaleFactor) {
return;
}
@@ -6667,7 +6695,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
@@ -6678,7 +6706,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
!PRESERVE_WINDOWS);
}
} finally {
- mWindowManager.continueSurfaceLayout();
+ continueWindowLayout();
}
}
}
diff --git a/services/core/java/com/android/server/wm/AnimationAdapter.java b/services/core/java/com/android/server/wm/AnimationAdapter.java
index be8a0bd7ad32..278a9ba641ab 100644
--- a/services/core/java/com/android/server/wm/AnimationAdapter.java
+++ b/services/core/java/com/android/server/wm/AnimationAdapter.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-import android.annotation.ColorInt;
import android.util.proto.ProtoOutputStream;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
@@ -41,11 +40,6 @@ interface AnimationAdapter {
boolean getShowWallpaper();
/**
- * @return The background color behind the animation.
- */
- @ColorInt int getBackgroundColor();
-
- /**
* Requests to start the animation.
*
* @param animationLeash The surface to run the animation on. See {@link SurfaceAnimator} for an
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 557a609dbc7b..66d52cc9bf5a 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -1999,8 +1999,8 @@ public class AppTransition implements Dump {
mNextAppTransitionFutureCallback, null /* finishedCallback */,
mNextAppTransitionScaleUp);
mNextAppTransitionFutureCallback = null;
+ mService.requestTraversal();
}
- mService.requestTraversal();
});
}
}
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index ffd9021989b4..f647fe46f067 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -955,6 +955,9 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
updateReportedVisibilityLocked();
}
+ // Reset the last saved PiP snap fraction on removal.
+ mDisplayContent.mPinnedStackControllerLocked.resetReentrySnapFraction(mActivityComponent);
+
mRemovingFromDisplay = false;
}
@@ -1021,7 +1024,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
if (DEBUG_ADD_REMOVE) Slog.v(TAG, "notifyAppStopped: " + this);
mAppStopped = true;
// Reset the last saved PiP snap fraction on app stop.
- mDisplayContent.mPinnedStackControllerLocked.resetReentrySnapFraction(this);
+ mDisplayContent.mPinnedStackControllerLocked.resetReentrySnapFraction(mActivityComponent);
destroySurfaces();
// Remove any starting window that was added for this app if they are still around.
removeStartingWindow();
@@ -1705,10 +1708,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
return;
}
- if (prevWinMode != WINDOWING_MODE_UNDEFINED && winMode == WINDOWING_MODE_PINNED) {
- // Entering PiP from fullscreen, reset the snap fraction
- mDisplayContent.mPinnedStackControllerLocked.resetReentrySnapFraction(this);
- } else if (prevWinMode == WINDOWING_MODE_PINNED && winMode != WINDOWING_MODE_UNDEFINED
+ if (prevWinMode == WINDOWING_MODE_PINNED && winMode != WINDOWING_MODE_UNDEFINED
&& !isHidden()) {
// Leaving PiP to fullscreen, save the snap fraction based on the pre-animation bounds
// for the next re-entry into PiP (assuming the activity is not hidden or destroyed)
@@ -1726,8 +1726,8 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
stackBounds = mTmpRect;
pinnedStack.getBounds(stackBounds);
}
- mDisplayContent.mPinnedStackControllerLocked.saveReentrySnapFraction(this,
- stackBounds);
+ mDisplayContent.mPinnedStackControllerLocked.saveReentrySnapFraction(
+ mActivityComponent, stackBounds);
}
} else if (shouldStartChangeTransition(prevWinMode, winMode)) {
initializeChangeTransition(mTmpPrevBounds);
@@ -2503,14 +2503,18 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
@Override
public SurfaceControl getAnimationLeashParent() {
- // All normal app transitions take place in an animation layer which is below the pinned
- // stack but may be above the parent stacks of the given animating apps.
// For transitions in the pinned stack (menu activity) we just let them occur as a child
// of the pinned stack.
- if (!inPinnedWindowingMode()) {
- return getAppAnimationLayer();
- } else {
+ // All normal app transitions take place in an animation layer which is below the pinned
+ // stack but may be above the parent stacks of the given animating apps by default. When
+ // a new hierarchical animation is enabled, we just let them occur as a child of the parent
+ // stack, i.e. the hierarchy of the surfaces is unchanged.
+ if (inPinnedWindowingMode()) {
return getStack().getSurfaceControl();
+ } else if (WindowManagerService.sHierarchicalAnimations) {
+ return super.getAnimationLeashParent();
+ } else {
+ return getAppAnimationLayer();
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 50ee9b9c808f..63ff2ea8069c 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;
@@ -582,27 +583,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
};
- private final Consumer<WindowState> mUpdateWallpaperForAnimator = w -> {
- final WindowStateAnimator winAnimator = w.mWinAnimator;
- if (winAnimator.mSurfaceController == null || !winAnimator.hasSurface()) {
- return;
- }
-
- // If this window is animating, ensure the animation background is set.
- final AnimationAdapter anim = w.mAppToken != null
- ? w.mAppToken.getAnimation()
- : w.getAnimation();
- if (anim != null) {
- final int color = anim.getBackgroundColor();
- if (color != 0) {
- final TaskStack stack = w.getStack();
- if (stack != null) {
- stack.setAnimationBackground(winAnimator, color);
- }
- }
- }
- };
-
private final Consumer<WindowState> mScheduleToastTimeout = w -> {
final int lostFocusUid = mTmpWindow.mOwnerUid;
final Handler handler = mWmService.mH;
@@ -2166,7 +2146,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
* so only need to configure display.
*/
void setForcedDensity(int density, int userId) {
- final boolean clear = density == mInitialDisplayDensity;
final boolean updateCurrent = userId == UserHandle.USER_CURRENT;
if (mWmService.mCurrentUserId == userId || updateCurrent) {
mBaseDisplayDensity = density;
@@ -2383,12 +2362,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mDisplayPolicy.switchUser();
}
- private void resetAnimationBackgroundAnimator() {
- for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- mTaskStackContainers.getChildAt(stackNdx).resetAnimationBackgroundAnimator();
- }
- }
-
@Override
void removeIfPossible() {
if (isAnimating()) {
@@ -2920,9 +2893,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);
@@ -3420,14 +3395,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
forAllWindows(mUpdateWindowsForAnimator, true /* traverseTopToBottom */);
}
- /**
- * Updates the {@link TaskStack#setAnimationBackground} for all windows.
- */
- void updateBackgroundForAnimator() {
- resetAnimationBackgroundAnimator();
- forAllWindows(mUpdateWallpaperForAnimator, true /* traverseTopToBottom */);
- }
-
boolean isInputMethodClientFocus(int uid, int pid) {
final WindowState imFocus = computeImeTarget(false /* updateImeTarget */);
if (imFocus == null) {
@@ -3881,21 +3848,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
}
- private static final class ScreenshotApplicationState {
- WindowState appWin;
- int maxLayer;
- int minLayer;
- boolean screenshotReady;
-
- void reset(boolean screenshotReady) {
- appWin = null;
- maxLayer = 0;
- minLayer = 0;
- this.screenshotReady = screenshotReady;
- minLayer = (screenshotReady) ? 0 : Integer.MAX_VALUE;
- }
- }
-
/**
* Base class for any direct child window container of {@link #DisplayContent} need to inherit
* from. This is mainly a pass through class that allows {@link #DisplayContent} to have
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index b502bd54bfc5..10d48c4d5282 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;
@@ -336,7 +335,6 @@ public class DisplayPolicy {
private static final Rect sTmpDisplayCutoutSafeExceptMaybeBarsRect = new Rect();
private static final Rect sTmpRect = new Rect();
- private static final Rect sTmpDockedFrame = new Rect();
private static final Rect sTmpNavFrame = new Rect();
private static final Rect sTmpLastParentFrame = new Rect();
@@ -2597,7 +2595,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/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 120ce3eb146e..ae3b5f2f70d3 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -36,7 +36,6 @@ import static com.android.server.wm.AppTransition.TOUCH_RESPONSE_INTERPOLATOR;
import static com.android.server.wm.DockedStackDividerControllerProto.MINIMIZED_DOCK;
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.LAYER_OFFSET_DIM;
import android.content.Context;
import android.content.res.Configuration;
@@ -141,8 +140,6 @@ public class DockedStackDividerController {
float mLastDividerProgress;
private final DividerSnapAlgorithm[] mSnapAlgorithmForRotation = new DividerSnapAlgorithm[4];
private boolean mImeHideRequested;
- private final Rect mLastDimLayerRect = new Rect();
- private float mLastDimLayerAlpha;
private TaskStack mDimmedStack;
DockedStackDividerController(WindowManagerService service, DisplayContent displayContent) {
@@ -656,14 +653,6 @@ public class DockedStackDividerController {
}
/**
- * @return The layer used for dimming the apps when dismissing docked/fullscreen stack. Just
- * above all application surfaces.
- */
- private int getResizeDimLayer() {
- return (mWindow != null) ? mWindow.mLayer - 1 : LAYER_OFFSET_DIM;
- }
-
- /**
* Notifies the docked stack divider controller of a visibility change that happens without
* an animation.
*/
diff --git a/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java b/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java
index 20a13334e564..c5c236416013 100644
--- a/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java
+++ b/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.UriGrantsManager;
import android.content.ClipData;
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 1b7b92bca250..842686441465 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() {
@@ -217,11 +238,6 @@ class InsetsSourceProvider {
}
@Override
- public int getBackgroundColor() {
- return 0;
- }
-
- @Override
public void startAnimation(SurfaceControl animationLeash, Transaction t,
OnAnimationFinishedCallback finishCallback) {
mCapturedLeash = animationLeash;
@@ -257,5 +273,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..2b5eb3ac29fb 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -71,7 +71,6 @@ class KeyguardController {
private boolean mAodShowing;
private boolean mKeyguardGoingAway;
private boolean mDismissalRequested;
- private int[] mSecondaryDisplayIdsShowing;
private int mBeforeUnoccludeTransit;
private int mVisibilityTransactionDepth;
private final SparseArray<KeyguardDisplayState> mDisplayStates = new SparseArray<>();
@@ -182,7 +181,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 +203,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);
@@ -328,9 +327,9 @@ class KeyguardController {
return;
}
- mWindowManager.onKeyguardOccludedChanged(isDisplayOccluded(DEFAULT_DISPLAY));
+ mWindowManager.mPolicy.onKeyguardOccludedChangedLw(isDisplayOccluded(DEFAULT_DISPLAY));
if (isKeyguardLocked()) {
- mWindowManager.deferSurfaceLayout();
+ mService.deferWindowLayout();
try {
mRootActivityContainer.getDefaultDisplay().mDisplayContent
.prepareAppTransition(resolveOccludeTransit(),
@@ -340,7 +339,7 @@ class KeyguardController {
mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
mWindowManager.executeAppTransition();
} finally {
- mWindowManager.continueSurfaceLayout();
+ mService.continueWindowLayout();
}
}
dismissDockedStackIfNeeded();
@@ -381,7 +380,7 @@ class KeyguardController {
* @return true if Keyguard can be currently dismissed without entering credentials.
*/
boolean canDismissKeyguard() {
- return mWindowManager.isKeyguardTrusted()
+ return mWindowManager.mPolicy.isKeyguardTrustedLw()
|| !mWindowManager.isKeyguardSecure(mService.getCurrentUserId());
}
@@ -516,7 +515,8 @@ class KeyguardController {
}
// TODO(b/123372519): isShowingDream can only works on default display.
if (mDisplayId == DEFAULT_DISPLAY) {
- mOccluded |= controller.mWindowManager.isShowingDream();
+ mOccluded |= mService.mRootActivityContainer.getDefaultDisplay().mDisplayContent
+ .getDisplayPolicy().isShowingDreamLw();
}
if (lastOccluded != mOccluded) {
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/LocalAnimationAdapter.java b/services/core/java/com/android/server/wm/LocalAnimationAdapter.java
index 77a024cc2e99..e67cb6fc6963 100644
--- a/services/core/java/com/android/server/wm/LocalAnimationAdapter.java
+++ b/services/core/java/com/android/server/wm/LocalAnimationAdapter.java
@@ -49,11 +49,6 @@ class LocalAnimationAdapter implements AnimationAdapter {
}
@Override
- public int getBackgroundColor() {
- return mSpec.getBackgroundColor();
- }
-
- @Override
public void startAnimation(SurfaceControl animationLeash, Transaction t,
OnAnimationFinishedCallback finishCallback) {
mAnimator.startAnimation(mSpec, animationLeash, t,
@@ -100,13 +95,6 @@ class LocalAnimationAdapter implements AnimationAdapter {
}
/**
- * @see AnimationAdapter#getBackgroundColor
- */
- default int getBackgroundColor() {
- return 0;
- }
-
- /**
* @see AnimationAdapter#getStatusBarTransitionsStartTime
*/
default long calculateStatusBarTransitionStartTime() {
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/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java
index ef0049b068f4..8e57fec6ba46 100644
--- a/services/core/java/com/android/server/wm/PinnedStackController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackController.java
@@ -27,6 +27,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.annotation.NonNull;
import android.app.RemoteAction;
+import android.content.ComponentName;
import android.content.pm.ParceledListSlice;
import android.content.res.Resources;
import android.graphics.Point;
@@ -50,7 +51,6 @@ import com.android.internal.util.Preconditions;
import com.android.server.UiThread;
import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
@@ -74,7 +74,7 @@ class PinnedStackController {
private static final String TAG = TAG_WITH_CLASS_NAME ? "PinnedStackController" : TAG_WM;
- public static final float INVALID_SNAP_FRACTION = -1f;
+ private static final float INVALID_SNAP_FRACTION = -1f;
private final WindowManagerService mService;
private final DisplayContent mDisplayContent;
private final Handler mHandler = UiThread.getHandler();
@@ -106,9 +106,6 @@ class PinnedStackController {
private int mDefaultStackGravity;
private float mDefaultAspectRatio;
private Point mScreenEdgeInsets;
- private int mCurrentMinSize;
- private float mReentrySnapFraction = INVALID_SNAP_FRACTION;
- private WeakReference<AppWindowToken> mLastPipActivity = null;
// The aspect ratio bounds of the PIP.
private float mMinAspectRatio;
@@ -118,7 +115,6 @@ class PinnedStackController {
private final DisplayMetrics mTmpMetrics = new DisplayMetrics();
private final Rect mTmpInsets = new Rect();
private final Rect mTmpRect = new Rect();
- private final Rect mTmpAnimatingBoundsRect = new Rect();
private final Point mTmpDisplaySize = new Point();
@@ -136,16 +132,19 @@ class PinnedStackController {
}
@Override
- public void setMinEdgeSize(int minEdgeSize) {
- mHandler.post(() -> {
- mCurrentMinSize = Math.max(mDefaultMinSize, minEdgeSize);
- });
+ public int getDisplayRotation() {
+ synchronized (mService.mGlobalLock) {
+ return mDisplayInfo.rotation;
+ }
}
@Override
- public int getDisplayRotation() {
+ public void startAnimation(Rect destinationBounds, Rect sourceRectHint,
+ int animationDuration) {
synchronized (mService.mGlobalLock) {
- return mDisplayInfo.rotation;
+ final TaskStack pinnedStack = mDisplayContent.getPinnedStack();
+ pinnedStack.animateResizePinnedStack(destinationBounds,
+ sourceRectHint, animationDuration, true /* fromFullscreen */);
}
}
}
@@ -188,7 +187,6 @@ class PinnedStackController {
final Resources res = mService.mContext.getResources();
mDefaultMinSize = res.getDimensionPixelSize(
com.android.internal.R.dimen.default_minimal_size_pip_resizable_task);
- mCurrentMinSize = mDefaultMinSize;
mDefaultAspectRatio = res.getFloat(
com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio);
final String screenEdgeInsetsDpString = res.getString(
@@ -216,6 +214,7 @@ class PinnedStackController {
listener.asBinder().linkToDeath(mPinnedStackListenerDeathHandler, 0);
listener.onListenerRegistered(mCallbacks);
mPinnedStackListener = listener;
+ notifyDisplayInfoChanged(mDisplayInfo);
notifyImeVisibilityChanged(mIsImeShowing, mImeHeight);
notifyShelfVisibilityChanged(mIsShelfShowing, mShelfHeight);
// The movement bounds notification needs to be sent before the minimized state, since
@@ -238,58 +237,34 @@ class PinnedStackController {
}
/**
- * Returns the current bounds (or the default bounds if there are no current bounds) with the
- * specified aspect ratio.
- */
- Rect transformBoundsToAspectRatio(Rect stackBounds, float aspectRatio,
- boolean useCurrentMinEdgeSize) {
- // Save the snap fraction, calculate the aspect ratio based on screen size
- final float snapFraction = mSnapAlgorithm.getSnapFraction(stackBounds,
- getMovementBounds(stackBounds));
-
- final int minEdgeSize = useCurrentMinEdgeSize ? mCurrentMinSize : mDefaultMinSize;
- final Size size = mSnapAlgorithm.getSizeForAspectRatio(aspectRatio, minEdgeSize,
- mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
- final int left = (int) (stackBounds.centerX() - size.getWidth() / 2f);
- final int top = (int) (stackBounds.centerY() - size.getHeight() / 2f);
- stackBounds.set(left, top, left + size.getWidth(), top + size.getHeight());
- mSnapAlgorithm.applySnapFraction(stackBounds, getMovementBounds(stackBounds), snapFraction);
- if (mIsMinimized) {
- applyMinimizedOffset(stackBounds, getMovementBounds(stackBounds));
- }
- return stackBounds;
- }
-
- /**
* Saves the current snap fraction for re-entry of the current activity into PiP.
*/
- void saveReentrySnapFraction(final AppWindowToken token, final Rect stackBounds) {
- mReentrySnapFraction = getSnapFraction(stackBounds);
- mLastPipActivity = new WeakReference<>(token);
+ void saveReentrySnapFraction(final ComponentName componentName, final Rect stackBounds) {
+ if (mPinnedStackListener == null) return;
+ try {
+ mPinnedStackListener.onSaveReentrySnapFraction(componentName, stackBounds);
+ } catch (RemoteException e) {
+ Slog.e(TAG_WM, "Error delivering save reentry fraction event.", e);
+ }
}
/**
* Resets the last saved snap fraction so that the default bounds will be returned.
*/
- void resetReentrySnapFraction(AppWindowToken token) {
- if (mLastPipActivity != null && mLastPipActivity.get() == token) {
- mReentrySnapFraction = INVALID_SNAP_FRACTION;
- mLastPipActivity = null;
+ void resetReentrySnapFraction(ComponentName componentName) {
+ if (mPinnedStackListener == null) return;
+ try {
+ mPinnedStackListener.onResetReentrySnapFraction(componentName);
+ } catch (RemoteException e) {
+ Slog.e(TAG_WM, "Error delivering reset reentry fraction event.", e);
}
}
/**
- * @return the default bounds to show the PIP when there is no active PIP.
- */
- Rect getDefaultOrLastSavedBounds() {
- return getDefaultBounds(mReentrySnapFraction);
- }
-
- /**
* @return the default bounds to show the PIP, if a {@param snapFraction} is provided, then it
* will apply the default bounds to the provided snap fraction.
*/
- Rect getDefaultBounds(float snapFraction) {
+ private Rect getDefaultBounds(float snapFraction) {
synchronized (mService.mGlobalLock) {
final Rect insetBounds = new Rect();
getInsetBounds(insetBounds);
@@ -311,13 +286,18 @@ class PinnedStackController {
}
}
+ private void setDisplayInfo(DisplayInfo displayInfo) {
+ mDisplayInfo.copyFrom(displayInfo);
+ notifyDisplayInfoChanged(mDisplayInfo);
+ }
+
/**
* In the case where the display rotation is changed but there is no stack, we can't depend on
* onTaskStackBoundsChanged() to be called. But we still should update our known display info
* with the new state so that we can update SystemUI.
*/
synchronized void onDisplayInfoChanged(DisplayInfo displayInfo) {
- mDisplayInfo.copyFrom(displayInfo);
+ setDisplayInfo(displayInfo);
notifyMovementBoundsChanged(false /* fromImeAdjustment */, false /* fromShelfAdjustment */);
}
@@ -335,7 +315,7 @@ class PinnedStackController {
} else if (targetBounds.isEmpty()) {
// The stack is null, we are just initializing the stack, so just store the display
// info and ignore
- mDisplayInfo.copyFrom(displayInfo);
+ setDisplayInfo(displayInfo);
outBounds.setEmpty();
return false;
}
@@ -345,7 +325,8 @@ class PinnedStackController {
// Calculate the snap fraction of the current stack along the old movement bounds
final float snapFraction = getSnapFraction(postChangeStackBounds);
- mDisplayInfo.copyFrom(displayInfo);
+
+ setDisplayInfo(displayInfo);
// Calculate the stack bounds in the new orientation to the same same fraction along the
// rotated movement bounds.
@@ -406,8 +387,11 @@ class PinnedStackController {
void setAspectRatio(float aspectRatio) {
if (Float.compare(mAspectRatio, aspectRatio) != 0) {
mAspectRatio = aspectRatio;
+ notifyAspectRatioChanged(aspectRatio);
notifyMovementBoundsChanged(false /* fromImeAdjustment */,
false /* fromShelfAdjustment */);
+ notifyPrepareAnimation(null /* sourceHintRect */, aspectRatio,
+ null /* stackBounds */);
}
}
@@ -429,6 +413,10 @@ class PinnedStackController {
notifyActionsChanged(mActions);
}
+ void prepareAnimation(Rect sourceRectHint, float aspectRatio, Rect stackBounds) {
+ notifyPrepareAnimation(sourceRectHint, aspectRatio, stackBounds);
+ }
+
private boolean isSameDimensionAndRotation(@NonNull DisplayInfo display1,
@NonNull DisplayInfo display2) {
Preconditions.checkNotNull(display1);
@@ -461,6 +449,15 @@ class PinnedStackController {
}
}
+ private void notifyAspectRatioChanged(float aspectRatio) {
+ if (mPinnedStackListener == null) return;
+ try {
+ mPinnedStackListener.onAspectRatioChanged(aspectRatio);
+ } catch (RemoteException e) {
+ Slog.e(TAG_WM, "Error delivering aspect ratio changed event.", e);
+ }
+ }
+
/**
* Notifies listeners that the PIP minimized state has changed.
*/
@@ -497,23 +494,13 @@ class PinnedStackController {
return;
}
try {
- final Rect insetBounds = new Rect();
- getInsetBounds(insetBounds);
- final Rect normalBounds = getDefaultBounds(INVALID_SNAP_FRACTION);
- if (isValidPictureInPictureAspectRatio(mAspectRatio)) {
- transformBoundsToAspectRatio(normalBounds, mAspectRatio,
- false /* useCurrentMinEdgeSize */);
- }
- final Rect animatingBounds = mTmpAnimatingBoundsRect;
+ final Rect animatingBounds = new Rect();
final TaskStack pinnedStack = mDisplayContent.getPinnedStack();
if (pinnedStack != null) {
pinnedStack.getAnimationOrCurrentBounds(animatingBounds);
- } else {
- animatingBounds.set(normalBounds);
}
- mPinnedStackListener.onMovementBoundsChanged(insetBounds, normalBounds,
- animatingBounds, fromImeAdjustment, fromShelfAdjustment,
- mDisplayInfo.rotation);
+ mPinnedStackListener.onMovementBoundsChanged(animatingBounds,
+ fromImeAdjustment, fromShelfAdjustment);
} catch (RemoteException e) {
Slog.e(TAG_WM, "Error delivering actions changed event.", e);
}
@@ -521,6 +508,30 @@ class PinnedStackController {
}
/**
+ * Notifies listeners that the PIP animation is about to happen.
+ */
+ private void notifyDisplayInfoChanged(DisplayInfo displayInfo) {
+ if (mPinnedStackListener == null) return;
+ try {
+ mPinnedStackListener.onDisplayInfoChanged(displayInfo);
+ } catch (RemoteException e) {
+ Slog.e(TAG_WM, "Error delivering DisplayInfo changed event.", e);
+ }
+ }
+
+ /**
+ * Notifies listeners that the PIP animation is about to happen.
+ */
+ private void notifyPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect stackBounds) {
+ if (mPinnedStackListener == null) return;
+ try {
+ mPinnedStackListener.onPrepareAnimation(sourceRectHint, aspectRatio, stackBounds);
+ } catch (RemoteException e) {
+ Slog.e(TAG_WM, "Error delivering prepare animation event.", e);
+ }
+ }
+
+ /**
* @return the bounds on the screen that the PIP can be visible in.
*/
private void getInsetBounds(Rect outRect) {
@@ -604,7 +615,6 @@ class PinnedStackController {
pw.println(prefix + " mImeHeight=" + mImeHeight);
pw.println(prefix + " mIsShelfShowing=" + mIsShelfShowing);
pw.println(prefix + " mShelfHeight=" + mShelfHeight);
- pw.println(prefix + " mReentrySnapFraction=" + mReentrySnapFraction);
pw.println(prefix + " mIsMinimized=" + mIsMinimized);
pw.println(prefix + " mAspectRatio=" + mAspectRatio);
pw.println(prefix + " mMinAspectRatio=" + mMinAspectRatio);
diff --git a/services/core/java/com/android/server/wm/PointerEventDispatcher.java b/services/core/java/com/android/server/wm/PointerEventDispatcher.java
index 8d08aa370b44..6b8144c69079 100644
--- a/services/core/java/com/android/server/wm/PointerEventDispatcher.java
+++ b/services/core/java/com/android/server/wm/PointerEventDispatcher.java
@@ -28,13 +28,11 @@ import com.android.server.UiThread;
import java.util.ArrayList;
public class PointerEventDispatcher extends InputEventReceiver {
- private final InputChannel mInputChannel;
private final ArrayList<PointerEventListener> mListeners = new ArrayList<>();
private PointerEventListener[] mListenersArray = new PointerEventListener[0];
public PointerEventDispatcher(InputChannel inputChannel) {
super(inputChannel, UiThread.getHandler().getLooper());
- mInputChannel = inputChannel;
}
@Override
@@ -94,7 +92,6 @@ public class PointerEventDispatcher extends InputEventReceiver {
@Override
public void dispose() {
super.dispose();
- mInputChannel.dispose();
synchronized (mListeners) {
mListeners.clear();
mListenersArray = null;
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..795a2ca67ac3 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -93,6 +93,8 @@ public class RecentsAnimationController implements DeathRecipient {
private IRecentsAnimationRunner mRunner;
private final RecentsAnimationCallbacks mCallbacks;
private final ArrayList<TaskAnimationAdapter> mPendingAnimations = new ArrayList<>();
+ private final ArrayList<WallpaperAnimationAdapter> mPendingWallpaperAnimations =
+ new ArrayList<>();
private final int mDisplayId;
private final Runnable mFailsafeRunnable = () ->
cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "failSafeRunnable");
@@ -434,6 +436,13 @@ public class RecentsAnimationController implements DeathRecipient {
mPendingAnimations.remove(taskAdapter);
}
+ @VisibleForTesting
+ void removeWallpaperAnimation(WallpaperAnimationAdapter wallpaperAdapter) {
+ if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "removeWallpaperAnimation()");
+ wallpaperAdapter.getLeashFinishedCallback().onAnimationFinished(wallpaperAdapter);
+ mPendingWallpaperAnimations.remove(wallpaperAdapter);
+ }
+
void startAnimation() {
if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "startAnimation(): mPendingStart=" + mPendingStart
+ " mCanceled=" + mCanceled);
@@ -442,25 +451,18 @@ public class RecentsAnimationController implements DeathRecipient {
return;
}
try {
- final ArrayList<RemoteAnimationTarget> appAnimations = new ArrayList<>();
- for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
- final TaskAnimationAdapter taskAdapter = mPendingAnimations.get(i);
- final RemoteAnimationTarget target = taskAdapter.createRemoteAnimationApp();
- if (target != null) {
- appAnimations.add(target);
- } else {
- removeAnimation(taskAdapter);
- }
- }
+ // Create the app targets
+ final RemoteAnimationTarget[] appTargets = createAppAnimations();
// Skip the animation if there is nothing to animate
- if (appAnimations.isEmpty()) {
+ if (appTargets.length == 0) {
cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "startAnimation-noAppWindows");
return;
}
- final RemoteAnimationTarget[] appTargets = appAnimations.toArray(
- new RemoteAnimationTarget[appAnimations.size()]);
+ // Create the wallpaper targets
+ final RemoteAnimationTarget[] wallpaperTargets = createWallpaperAnimations();
+
mPendingStart = false;
// Perform layout if it was scheduled before to make sure that we get correct content
@@ -479,7 +481,8 @@ public class RecentsAnimationController implements DeathRecipient {
mService.getStableInsets(mDisplayId, mTmpRect);
contentInsets = mTmpRect;
}
- mRunner.onAnimationStart(mController, appTargets, contentInsets, minimizedHomeBounds);
+ mRunner.onAnimationStart(mController, appTargets, wallpaperTargets, contentInsets,
+ minimizedHomeBounds);
if (DEBUG_RECENTS_ANIMATIONS) {
Slog.d(TAG, "startAnimation(): Notify animation start:");
for (int i = 0; i < mPendingAnimations.size(); i++) {
@@ -495,6 +498,32 @@ public class RecentsAnimationController implements DeathRecipient {
mService.mAtmInternal.notifyAppTransitionStarting(reasons, SystemClock.uptimeMillis());
}
+ private RemoteAnimationTarget[] createAppAnimations() {
+ final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>();
+ for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
+ final TaskAnimationAdapter taskAdapter = mPendingAnimations.get(i);
+ final RemoteAnimationTarget target = taskAdapter.createRemoteAnimationTarget();
+ if (target != null) {
+ targets.add(target);
+ } else {
+ removeAnimation(taskAdapter);
+ }
+ }
+ return targets.toArray(new RemoteAnimationTarget[targets.size()]);
+ }
+
+ private RemoteAnimationTarget[] createWallpaperAnimations() {
+ if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "createWallpaperAnimations()");
+ return WallpaperAnimationAdapter.startWallpaperAnimations(mService, 0L, 0L,
+ adapter -> {
+ synchronized (mService.mGlobalLock) {
+ // If the wallpaper animation is canceled, continue with the recents
+ // animation
+ mPendingWallpaperAnimations.remove(adapter);
+ }
+ }, mPendingWallpaperAnimations);
+ }
+
void cancelAnimation(@ReorderMode int reorderMode, String reason) {
cancelAnimation(reorderMode, false /*screenshot */, reason);
}
@@ -619,6 +648,11 @@ public class RecentsAnimationController implements DeathRecipient {
removeAnimation(taskAdapter);
}
+ for (int i = mPendingWallpaperAnimations.size() - 1; i >= 0; i--) {
+ final WallpaperAnimationAdapter wallpaperAdapter = mPendingWallpaperAnimations.get(i);
+ removeWallpaperAnimation(wallpaperAdapter);
+ }
+
// Clear any pending failsafe runnables
mService.mH.removeCallbacks(mFailsafeRunnable);
mDisplayContent.mAppTransition.unregisterListener(mAppTransitionListener);
@@ -747,6 +781,15 @@ public class RecentsAnimationController implements DeathRecipient {
return false;
}
+ boolean isAnimatingWallpaper(WallpaperWindowToken token) {
+ for (int i = mPendingWallpaperAnimations.size() - 1; i >= 0; i--) {
+ if (token == mPendingWallpaperAnimations.get(i).getToken()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private boolean isAnimatingApp(AppWindowToken appToken) {
for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
final Task task = mPendingAnimations.get(i).mTask;
@@ -760,6 +803,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 {
@@ -779,7 +827,7 @@ public class RecentsAnimationController implements DeathRecipient {
mBounds.set(container.getDisplayedBounds());
}
- RemoteAnimationTarget createRemoteAnimationApp() {
+ RemoteAnimationTarget createRemoteAnimationTarget() {
final AppWindowToken topApp = mTask.getTopVisibleAppToken();
final WindowState mainWindow = topApp != null
? topApp.findMainWindow()
@@ -806,11 +854,6 @@ public class RecentsAnimationController implements DeathRecipient {
}
@Override
- public int getBackgroundColor() {
- return 0;
- }
-
- @Override
public void startAnimation(SurfaceControl animationLeash, Transaction t,
OnAnimationFinishedCallback finishCallback) {
// Restore z-layering, position and stack crop until client has a chance to modify it.
@@ -825,6 +868,7 @@ public class RecentsAnimationController implements DeathRecipient {
@Override
public void onAnimationCancelled(SurfaceControl animationLeash) {
+ // Cancel the animation immediately if any single task animator is canceled
cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "taskAnimationAdapterCanceled");
}
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 7448e007f2c5..87bda4a545e1 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -58,6 +58,8 @@ class RemoteAnimationController implements DeathRecipient {
private final WindowManagerService mService;
private final RemoteAnimationAdapter mRemoteAnimationAdapter;
private final ArrayList<RemoteAnimationRecord> mPendingAnimations = new ArrayList<>();
+ private final ArrayList<WallpaperAnimationAdapter> mPendingWallpaperAnimations =
+ new ArrayList<>();
private final Rect mTmpRect = new Rect();
private final Handler mHandler;
private final Runnable mTimeoutRunnable = () -> cancelAnimation("timeoutRunnable");
@@ -110,16 +112,21 @@ class RemoteAnimationController implements DeathRecipient {
(long) (TIMEOUT_MS * mService.getCurrentAnimatorScale()));
mFinishedCallback = new FinishedCallback(this);
- final RemoteAnimationTarget[] animations = createAnimations();
- if (animations.length == 0) {
+ // Create the app targets
+ final RemoteAnimationTarget[] appTargets = createAppAnimations();
+ if (appTargets.length == 0) {
if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "goodToGo(): No apps to animate");
onAnimationFinished();
return;
}
+
+ // Create the remote wallpaper animation targets (if any)
+ final RemoteAnimationTarget[] wallpaperTargets = createWallpaperAnimations();
mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
try {
linkToDeathOfRunner();
- mRemoteAnimationAdapter.getRunner().onAnimationStart(animations, mFinishedCallback);
+ mRemoteAnimationAdapter.getRunner().onAnimationStart(appTargets, wallpaperTargets,
+ mFinishedCallback);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to start remote animation", e);
onAnimationFinished();
@@ -155,8 +162,8 @@ class RemoteAnimationController implements DeathRecipient {
Slog.i(TAG, sw.toString());
}
- private RemoteAnimationTarget[] createAnimations() {
- if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "createAnimations()");
+ private RemoteAnimationTarget[] createAppAnimations() {
+ if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "createAppAnimations()");
final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>();
for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
final RemoteAnimationRecord wrappers = mPendingAnimations.get(i);
@@ -186,6 +193,19 @@ class RemoteAnimationController implements DeathRecipient {
return targets.toArray(new RemoteAnimationTarget[targets.size()]);
}
+ private RemoteAnimationTarget[] createWallpaperAnimations() {
+ if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "createWallpaperAnimations()");
+ return WallpaperAnimationAdapter.startWallpaperAnimations(mService,
+ mRemoteAnimationAdapter.getDuration(),
+ mRemoteAnimationAdapter.getStatusBarTransitionDelay(),
+ adapter -> {
+ synchronized (mService.mGlobalLock) {
+ // If the wallpaper animation is canceled, continue with the app animation
+ mPendingWallpaperAnimations.remove(adapter);
+ }
+ }, mPendingWallpaperAnimations);
+ }
+
private void onAnimationFinished() {
if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "onAnimationFinished(): mPendingAnimations="
+ mPendingAnimations.size());
@@ -207,7 +227,15 @@ class RemoteAnimationController implements DeathRecipient {
adapters.mThumbnailAdapter.mCapturedFinishCallback
.onAnimationFinished(adapters.mThumbnailAdapter);
}
- if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "\t" + adapters.mAppWindowToken);
+ mPendingAnimations.remove(i);
+ if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "\tapp=" + adapters.mAppWindowToken);
+ }
+
+ for (int i = mPendingWallpaperAnimations.size() - 1; i >= 0; i--) {
+ final WallpaperAnimationAdapter adapter = mPendingWallpaperAnimations.get(i);
+ adapter.getLeashFinishedCallback().onAnimationFinished(adapter);
+ mPendingWallpaperAnimations.remove(i);
+ if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "\twallpaper=" + adapter.getToken());
}
} catch (Exception e) {
Slog.e(TAG, "Failed to finish remote animation", e);
@@ -390,11 +418,6 @@ class RemoteAnimationController implements DeathRecipient {
}
@Override
- public int getBackgroundColor() {
- return 0;
- }
-
- @Override
public void startAnimation(SurfaceControl animationLeash, Transaction t,
OnAnimationFinishedCallback finishCallback) {
if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "startAnimation");
@@ -424,10 +447,7 @@ class RemoteAnimationController implements DeathRecipient {
mPendingAnimations.remove(mRecord);
}
if (mPendingAnimations.isEmpty()) {
- mHandler.removeCallbacks(mTimeoutRunnable);
- releaseFinishedCallback();
- invokeAnimationCancelled();
- setRunningRemoteAnimation(false);
+ cancelAnimation("allAppAnimationsCanceled");
}
}
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
index d29cc1dbb585..734f2248d131 100644
--- a/services/core/java/com/android/server/wm/RootActivityContainer.java
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -193,6 +193,9 @@ class RootActivityContainer extends ConfigurationContainer
/** Set when a power hint has started, but not ended. */
private boolean mPowerHintSent;
+ /** Used to keep ensureActivitiesVisible() from being entered recursively. */
+ private boolean mInEnsureActivitiesVisible = false;
+
// The default minimal size that will be used if the activity doesn't specify its minimal size.
// It will be calculated when the default display gets added.
int mDefaultMinSizeOfResizeableTaskDp = -1;
@@ -805,8 +808,14 @@ class RootActivityContainer extends ConfigurationContainer
*/
void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
boolean preserveWindows, boolean notifyClients) {
- mStackSupervisor.getKeyguardController().beginActivityVisibilityUpdate();
+ if (mInEnsureActivitiesVisible) {
+ // Don't do recursive work.
+ return;
+ }
+ mInEnsureActivitiesVisible = true;
+
try {
+ mStackSupervisor.getKeyguardController().beginActivityVisibilityUpdate();
// First the front stacks. In case any are not fullscreen and are in front of home.
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ActivityDisplay display = mActivityDisplays.get(displayNdx);
@@ -815,6 +824,7 @@ class RootActivityContainer extends ConfigurationContainer
}
} finally {
mStackSupervisor.getKeyguardController().endActivityVisibilityUpdate();
+ mInEnsureActivitiesVisible = false;
}
}
@@ -945,8 +955,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();
@@ -960,10 +969,6 @@ class RootActivityContainer extends ConfigurationContainer
// Need to make sure the pinned stack exist so we can resize it below...
stack = display.getOrCreateStack(WINDOWING_MODE_PINNED, r.getActivityType(), ON_TOP);
- // Calculate the target bounds here before the task is reparented back into pinned windowing
- // mode (which will reset the saved bounds)
- final Rect destBounds = stack.getDefaultPictureInPictureBounds(aspectRatio);
-
try {
final TaskRecord task = r.getTaskRecord();
// Resize the pinned stack to match the current size of the task the activity we are
@@ -999,12 +1004,17 @@ class RootActivityContainer extends ConfigurationContainer
// to the pinned stack
r.supportsEnterPipOnTaskSwitch = false;
} finally {
- mWindowManager.continueSurfaceLayout();
+ mService.continueWindowLayout();
}
- stack.animateResizePinnedStack(sourceHintBounds, destBounds, -1 /* animationDuration */,
- true /* fromFullscreen */);
+ // Notify the pinned stack controller to prepare the PiP animation, expect callback
+ // delivered from SystemUI to WM to start the animation.
+ final PinnedStackController pinnedStackController =
+ display.mDisplayContent.getPinnedStackController();
+ pinnedStackController.prepareAnimation(sourceHintBounds, aspectRatio,
+ null /* stackBounds */);
+ // TODO: revisit the following statement after the animation is moved from WM to SysUI.
// Update the visibility of all activities after the they have been reparented to the new
// stack. This MUST run after the animation above is scheduled to ensure that the windows
// drawn signal is scheduled after the bounds animation start call on the bounds animator
@@ -1615,7 +1625,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 +1635,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 +1675,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 +2082,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 +2103,7 @@ class RootActivityContainer extends ConfigurationContainer
}
}
} finally {
- mWindowManager.continueSurfaceLayout();
+ mService.continueWindowLayout();
}
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index d0b6fc890ca0..4365d0325545 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -472,8 +472,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
final int count = mChildren.size();
for (int i = 0; i < count; ++i) {
- final DisplayContent dc = mChildren.get(i);
- final int pendingChanges = animator.getPendingLayoutChanges(dc.getDisplayId());
+ final int pendingChanges = mChildren.get(i).pendingLayoutChanges;
if ((pendingChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
animator.mBulkUpdateParams |= SET_WALLPAPER_ACTION_PENDING;
}
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/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index cd211a28a908..ba728ba18d57 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -54,6 +54,7 @@ class SurfaceAnimator {
final Animatable mAnimatable;
private final OnAnimationFinishedCallback mInnerAnimationFinishedCallback;
@VisibleForTesting
+ @Nullable
final Runnable mAnimationFinishedCallback;
private boolean mAnimationStartDelayed;
@@ -262,7 +263,7 @@ class SurfaceAnimator {
if (!mAnimationStartDelayed && forwardCancel) {
animation.onAnimationCancelled(leash);
}
- if (!restarting) {
+ if (!restarting && mAnimationFinishedCallback != null) {
mAnimationFinishedCallback.run();
}
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 3a2eb57f1d80..85ba80602bdb 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -85,8 +85,6 @@ class Task extends WindowContainer<AppWindowToken> implements ConfigurationConta
private Rect mTmpRect = new Rect();
// For handling display rotations.
private Rect mTmpRect2 = new Rect();
- // For retrieving dim bounds
- private Rect mTmpRect3 = new Rect();
// Resize mode of the task. See {@link ActivityInfo#resizeMode}
private int mResizeMode;
@@ -613,6 +611,9 @@ class Task extends WindowContainer<AppWindowToken> implements ConfigurationConta
@Override
public SurfaceControl getAnimationLeashParent() {
+ if (!WindowManagerService.sHierarchicalAnimations) {
+ return super.getAnimationLeashParent();
+ }
// Currently, only the recents animation will create animation leashes for tasks. In this
// case, reparent the task to the home animation layer while it is being animated to allow
// the home activity to reorder the app windows relative to its own.
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 8b0b6ce8ce32..42866f97b4b8 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -64,10 +64,6 @@ class TaskPositioner implements IBinder.DeathRecipient {
private static Factory sFactory;
- // The margin the pointer position has to be within the side of the screen to be
- // considered at the side of the screen.
- static final int SIDE_MARGIN_DIP = 100;
-
@IntDef(flag = true,
value = {
CTRL_NONE,
@@ -101,7 +97,6 @@ class TaskPositioner implements IBinder.DeathRecipient {
private DisplayContent mDisplayContent;
private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
private Rect mTmpRect = new Rect();
- private int mSideMargin;
private int mMinVisibleWidth;
private int mMinVisibleHeight;
@@ -309,7 +304,6 @@ class TaskPositioner implements IBinder.DeathRecipient {
// Notify InputMonitor to take mDragWindowHandle.
mDisplayContent.getInputMonitor().updateInputWindowsLw(true /*force*/);
- mSideMargin = dipToPixel(SIDE_MARGIN_DIP, mDisplayMetrics);
mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, mDisplayMetrics);
mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, mDisplayMetrics);
display.getRealSize(mMaxVisibleSize);
@@ -488,12 +482,6 @@ class TaskPositioner implements IBinder.DeathRecipient {
int right = mWindowOriginalBounds.right;
int bottom = mWindowOriginalBounds.bottom;
- // The aspect which we have to respect. Note that if the orientation does not need to be
- // preserved the aspect will be calculated as 1.0 which neutralizes the following
- // computations.
- final float minAspect = !mPreserveOrientation
- ? 1.0f
- : (mStartOrientationWasLandscape ? MIN_ASPECT : (1.0f / MIN_ASPECT));
// Calculate the resulting width and height of the drag operation.
int width = right - left;
int height = bottom - top;
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index d3f3981625d9..0ea108e30280 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) {
@@ -1085,8 +1085,8 @@ class TaskRecord extends ConfigurationContainer {
clearRootProcess();
- // TODO: Use window container controller once tasks are better synced between AM and WM
- mService.mWindowManager.notifyTaskRemovedFromRecents(taskId, userId);
+ mService.mWindowManager.mTaskSnapshotController.notifyTaskRemovedFromRecents(
+ taskId, userId);
}
void setTaskToAffiliateWith(TaskRecord taskToAffiliateWith) {
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 239bd004705f..10d8328a7535 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -45,7 +45,6 @@ import static com.android.server.wm.StackProto.ADJUSTED_FOR_IME;
import static com.android.server.wm.StackProto.ADJUST_DIVIDER_AMOUNT;
import static com.android.server.wm.StackProto.ADJUST_IME_AMOUNT;
import static com.android.server.wm.StackProto.ANIMATING_BOUNDS;
-import static com.android.server.wm.StackProto.ANIMATION_BACKGROUND_SURFACE_IS_DIMMING;
import static com.android.server.wm.StackProto.BOUNDS;
import static com.android.server.wm.StackProto.DEFER_REMOVAL;
import static com.android.server.wm.StackProto.FILLS_PARENT;
@@ -111,12 +110,6 @@ public class TaskStack extends WindowContainer<Task> implements
*/
private final Rect mFullyAdjustedImeBounds = new Rect();
- private SurfaceControl mAnimationBackgroundSurface;
- private boolean mAnimationBackgroundSurfaceIsShown = false;
-
- /** The particular window with an Animation with non-zero background color. */
- private WindowStateAnimator mAnimationBackgroundAnimator;
-
/** Application tokens that are exiting, but still on screen for animations. */
final AppTokenList mExitingAppTokens = new AppTokenList();
final AppTokenList mTmpAppTokens = new AppTokenList();
@@ -230,39 +223,6 @@ public class TaskStack extends WindowContainer<Task> implements
}
}
- private void updateAnimationBackgroundBounds() {
- if (mAnimationBackgroundSurface == null) {
- return;
- }
- getRawBounds(mTmpRect);
- final Rect stackBounds = getBounds();
- getPendingTransaction()
- .setWindowCrop(mAnimationBackgroundSurface, mTmpRect.width(), mTmpRect.height())
- .setPosition(mAnimationBackgroundSurface, mTmpRect.left - stackBounds.left,
- mTmpRect.top - stackBounds.top);
- scheduleAnimation();
- }
-
- private void hideAnimationSurface() {
- if (mAnimationBackgroundSurface == null) {
- return;
- }
- getPendingTransaction().hide(mAnimationBackgroundSurface);
- mAnimationBackgroundSurfaceIsShown = false;
- scheduleAnimation();
- }
-
- private void showAnimationSurface(float alpha) {
- if (mAnimationBackgroundSurface == null) {
- return;
- }
- getPendingTransaction().setLayer(mAnimationBackgroundSurface, Integer.MIN_VALUE)
- .setAlpha(mAnimationBackgroundSurface, alpha)
- .show(mAnimationBackgroundSurface);
- mAnimationBackgroundSurfaceIsShown = true;
- scheduleAnimation();
- }
-
@Override
public int setBounds(Rect bounds) {
return setBounds(getRequestedOverrideBounds(), bounds);
@@ -275,10 +235,6 @@ public class TaskStack extends WindowContainer<Task> implements
final int result = super.setBounds(bounds);
- if (getParent() != null) {
- updateAnimationBackgroundBounds();
- }
-
updateAdjustedBounds();
updateSurfaceBounds();
@@ -738,7 +694,6 @@ public class TaskStack extends WindowContainer<Task> implements
// surface position.
updateSurfaceSize(getPendingTransaction());
final int windowingMode = getWindowingMode();
- final boolean isAlwaysOnTop = isAlwaysOnTop();
if (mDisplayContent == null) {
return;
@@ -822,11 +777,6 @@ public class TaskStack extends WindowContainer<Task> implements
super.onDisplayChanged(dc);
updateSurfaceBounds();
- if (mAnimationBackgroundSurface == null) {
- mAnimationBackgroundSurface = makeChildSurface(null).setColorLayer()
- .setName("animation background stackId=" + mStackId)
- .build();
- }
}
/**
@@ -1008,27 +958,10 @@ public class TaskStack extends WindowContainer<Task> implements
EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId);
- if (mAnimationBackgroundSurface != null) {
- mWmService.mTransactionFactory.get().remove(mAnimationBackgroundSurface).apply();
- mAnimationBackgroundSurface = null;
- }
-
mDisplayContent = null;
mWmService.mWindowPlacerLocked.requestTraversal();
}
- void resetAnimationBackgroundAnimator() {
- mAnimationBackgroundAnimator = null;
- hideAnimationSurface();
- }
-
- void setAnimationBackground(WindowStateAnimator winAnimator, int color) {
- if (mAnimationBackgroundAnimator == null) {
- mAnimationBackgroundAnimator = winAnimator;
- showAnimationSurface(((color >> 24) & 0xff) / 255f);
- }
- }
-
// TODO: Should each user have there own stacks?
@Override
void switchUser() {
@@ -1365,7 +1298,6 @@ public class TaskStack extends WindowContainer<Task> implements
}
proto.write(FILLS_PARENT, matchParentBounds());
getRawBounds().writeToProto(proto, BOUNDS);
- proto.write(ANIMATION_BACKGROUND_SURFACE_IS_DIMMING, mAnimationBackgroundSurfaceIsShown);
proto.write(DEFER_REMOVAL, mDeferRemoval);
proto.write(MINIMIZE_AMOUNT, mMinimizeAmount);
proto.write(ADJUSTED_FOR_IME, mAdjustedForIme);
@@ -1395,9 +1327,6 @@ public class TaskStack extends WindowContainer<Task> implements
for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; taskNdx--) {
mChildren.get(taskNdx).dump(pw, prefix + " ", dumpAll);
}
- if (mAnimationBackgroundSurfaceIsShown) {
- pw.println(prefix + "mWindowAnimationBackgroundSurface is shown");
- }
if (!mExitingAppTokens.isEmpty()) {
pw.println();
pw.println(" Exiting application tokens:");
@@ -1661,40 +1590,6 @@ public class TaskStack extends WindowContainer<Task> implements
}
/**
- * @return the current stack bounds transformed to the given {@param aspectRatio}. If
- * the default bounds is {@code null}, then the {@param aspectRatio} is applied to the
- * default bounds.
- */
- Rect getPictureInPictureBounds(float aspectRatio, Rect stackBounds) {
- if (!mWmService.mAtmService.mSupportsPictureInPicture) {
- return null;
- }
-
- final DisplayContent displayContent = getDisplayContent();
- if (displayContent == null) {
- return null;
- }
-
- if (!inPinnedWindowingMode()) {
- return null;
- }
-
- final PinnedStackController pinnedStackController =
- displayContent.getPinnedStackController();
- if (stackBounds == null) {
- // Calculate the aspect ratio bounds from the default bounds
- stackBounds = pinnedStackController.getDefaultOrLastSavedBounds();
- }
-
- if (pinnedStackController.isValidPictureInPictureAspectRatio(aspectRatio)) {
- return pinnedStackController.transformBoundsToAspectRatio(stackBounds, aspectRatio,
- true /* useCurrentMinEdgeSize */);
- } else {
- return stackBounds;
- }
- }
-
- /**
* Animates the pinned stack.
*/
void animateResizePinnedStack(Rect toBounds, Rect sourceHintBounds,
@@ -1771,6 +1666,11 @@ public class TaskStack extends WindowContainer<Task> implements
return;
}
+ final DisplayContent displayContent = getDisplayContent();
+ if (displayContent == null) {
+ return;
+ }
+
if (!inPinnedWindowingMode()) {
return;
}
@@ -1781,13 +1681,10 @@ public class TaskStack extends WindowContainer<Task> implements
if (Float.compare(aspectRatio, pinnedStackController.getAspectRatio()) == 0) {
return;
}
- getAnimationOrCurrentBounds(mTmpFromBounds);
- mTmpToBounds.set(mTmpFromBounds);
- getPictureInPictureBounds(aspectRatio, mTmpToBounds);
- if (!mTmpToBounds.equals(mTmpFromBounds)) {
- animateResizePinnedStack(mTmpToBounds, null /* sourceHintBounds */,
- -1 /* duration */, false /* fromFullscreen */);
- }
+
+ // Notify the pinned stack controller about aspect ratio change.
+ // This would result a callback delivered from SystemUI to WM to start animation,
+ // if the bounds are ought to be altered due to aspect ratio change.
pinnedStackController.setAspectRatio(
pinnedStackController.isValidPictureInPictureAspectRatio(aspectRatio)
? aspectRatio : -1f);
diff --git a/services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java b/services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java
new file mode 100644
index 000000000000..895350b43eeb
--- /dev/null
+++ b/services/core/java/com/android/server/wm/WallpaperAnimationAdapter.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.wm;
+
+import static com.android.server.wm.AnimationAdapterProto.REMOTE;
+import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_RECENTS_ANIMATIONS;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_REMOTE_ANIMATIONS;
+
+import android.graphics.Point;
+import android.os.SystemClock;
+import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
+import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.function.Consumer;
+
+/**
+ * An animation adapter for wallpaper windows.
+ */
+class WallpaperAnimationAdapter implements AnimationAdapter {
+ private static final String TAG = "WallpaperAnimationAdapter";
+
+ private final WallpaperWindowToken mWallpaperToken;
+ private SurfaceControl mCapturedLeash;
+ private SurfaceAnimator.OnAnimationFinishedCallback mCapturedLeashFinishCallback;
+
+ private long mDurationHint;
+ private long mStatusBarTransitionDelay;
+
+ private Consumer<WallpaperAnimationAdapter> mAnimationCanceledRunnable;
+ private RemoteAnimationTarget mTarget;
+
+ WallpaperAnimationAdapter(WallpaperWindowToken wallpaperToken,
+ long durationHint, long statusBarTransitionDelay,
+ Consumer<WallpaperAnimationAdapter> animationCanceledRunnable) {
+ mWallpaperToken = wallpaperToken;
+ mDurationHint = durationHint;
+ mStatusBarTransitionDelay = statusBarTransitionDelay;
+ mAnimationCanceledRunnable = animationCanceledRunnable;
+ }
+
+ /**
+ * Creates and starts remote animations for all the visible wallpaper windows.
+ *
+ * @return RemoteAnimationTarget[] targets for all the visible wallpaper windows
+ */
+ public static RemoteAnimationTarget[] startWallpaperAnimations(WindowManagerService service,
+ long durationHint, long statusBarTransitionDelay,
+ Consumer<WallpaperAnimationAdapter> animationCanceledRunnable,
+ ArrayList<WallpaperAnimationAdapter> adaptersOut) {
+ final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>();
+ service.mRoot.forAllWallpaperWindows(wallpaperWindow -> {
+ if (!wallpaperWindow.getDisplayContent().mWallpaperController.isWallpaperVisible()) {
+ if (DEBUG_REMOTE_ANIMATIONS || DEBUG_RECENTS_ANIMATIONS) {
+ Slog.d(TAG, "\tNot visible=" + wallpaperWindow);
+ }
+ return;
+ }
+
+ if (DEBUG_REMOTE_ANIMATIONS || DEBUG_RECENTS_ANIMATIONS) {
+ Slog.d(TAG, "\tvisible=" + wallpaperWindow);
+ }
+ final WallpaperAnimationAdapter wallpaperAdapter = new WallpaperAnimationAdapter(
+ wallpaperWindow, durationHint, statusBarTransitionDelay,
+ animationCanceledRunnable);
+ wallpaperWindow.startAnimation(wallpaperWindow.getPendingTransaction(),
+ wallpaperAdapter, false /* hidden */);
+ targets.add(wallpaperAdapter.createRemoteAnimationTarget());
+ adaptersOut.add(wallpaperAdapter);
+ });
+ return targets.toArray(new RemoteAnimationTarget[targets.size()]);
+ }
+
+ /**
+ * Create a remote animation target for this animation adapter.
+ */
+ RemoteAnimationTarget createRemoteAnimationTarget() {
+ mTarget = new RemoteAnimationTarget(-1, -1, getLeash(), false, null, null,
+ mWallpaperToken.getPrefixOrderIndex(), new Point(), null,
+ mWallpaperToken.getWindowConfiguration(), true, null, null);
+ return mTarget;
+ }
+
+ /**
+ * @return the leash for this animation (only valid after the wallpaper window surface animation
+ * has started).
+ */
+ SurfaceControl getLeash() {
+ return mCapturedLeash;
+ }
+
+ /**
+ * @return the callback to call to clean up when the animation has finished.
+ */
+ SurfaceAnimator.OnAnimationFinishedCallback getLeashFinishedCallback() {
+ return mCapturedLeashFinishCallback;
+ }
+
+ /**
+ * @return the wallpaper window
+ */
+ WallpaperWindowToken getToken() {
+ return mWallpaperToken;
+ }
+
+ @Override
+ public boolean getShowWallpaper() {
+ // Not used
+ return false;
+ }
+
+ @Override
+ public void startAnimation(SurfaceControl animationLeash, SurfaceControl.Transaction t,
+ SurfaceAnimator.OnAnimationFinishedCallback finishCallback) {
+ if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "startAnimation");
+
+ // Restore z-layering until client has a chance to modify it.
+ t.setLayer(animationLeash, mWallpaperToken.getPrefixOrderIndex());
+ mCapturedLeash = animationLeash;
+ mCapturedLeashFinishCallback = finishCallback;
+ }
+
+ @Override
+ public void onAnimationCancelled(SurfaceControl animationLeash) {
+ if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "onAnimationCancelled");
+ mAnimationCanceledRunnable.accept(this);
+ }
+
+ @Override
+ public long getDurationHint() {
+ return mDurationHint;
+ }
+
+ @Override
+ public long getStatusBarTransitionsStartTime() {
+ return SystemClock.uptimeMillis() + mStatusBarTransitionDelay;
+ }
+
+ @Override
+ public void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix);
+ pw.print("token=");
+ pw.println(mWallpaperToken);
+ if (mTarget != null) {
+ pw.print(prefix);
+ pw.println("Target:");
+ mTarget.dump(pw, prefix + " ");
+ } else {
+ pw.print(prefix);
+ pw.println("Target: null");
+ }
+ }
+
+ @Override
+ public void writeToProto(ProtoOutputStream proto) {
+ final long token = proto.start(REMOTE);
+ if (mTarget != null) {
+ mTarget.writeToProto(proto, TARGET);
+ }
+ proto.end(token);
+ }
+}
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 614727263469..13902eedbfba 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -110,7 +110,6 @@ class WallpaperController {
private final FindWallpaperTargetResult mFindResults = new FindWallpaperTargetResult();
private final ToBooleanFunction<WindowState> mFindWallpaperTargetFunction = w -> {
- final WindowAnimator winAnimator = mService.mAnimator;
if ((w.mAttrs.type == TYPE_WALLPAPER)) {
if (mFindResults.topWallpaper == null || mFindResults.resetTopWallpaper) {
mFindResults.setTopWallpaper(w);
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index e15b783b5606..528cece9a78b 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -29,6 +30,8 @@ import android.util.Slog;
import android.view.DisplayInfo;
import android.view.animation.Animation;
+import java.util.function.Consumer;
+
/**
* A token that represents a set of wallpaper windows.
*/
@@ -153,6 +156,11 @@ class WallpaperWindowToken extends WindowToken {
}
@Override
+ void forAllWallpaperWindows(Consumer<WallpaperWindowToken> callback) {
+ callback.accept(this);
+ }
+
+ @Override
public String toString() {
if (stringName == null) {
StringBuilder sb = new StringBuilder();
diff --git a/services/core/java/com/android/server/wm/WindowAnimationSpec.java b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
index d8ebd84b3e73..7c183a8bf739 100644
--- a/services/core/java/com/android/server/wm/WindowAnimationSpec.java
+++ b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
@@ -19,7 +19,6 @@ package com.android.server.wm;
import static com.android.server.wm.AnimationAdapter.STATUS_BAR_TRANSITION_DURATION;
import static com.android.server.wm.AnimationSpecProto.WINDOW;
import static com.android.server.wm.WindowAnimationSpecProto.ANIMATION;
-import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE;
import android.graphics.Point;
@@ -81,11 +80,6 @@ public class WindowAnimationSpec implements AnimationSpec {
}
@Override
- public int getBackgroundColor() {
- return mAnimation.getBackgroundColor();
- }
-
- @Override
public long getDuration() {
return mAnimation.computeDurationHint();
}
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 4fce46b9dfaf..c7916e829349 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -180,7 +180,6 @@ public class WindowAnimator {
// Update animations of all applications, including those
// associated with exiting/removed apps
dc.updateWindowsForAnimator();
- dc.updateBackgroundForAnimator();
dc.prepareSurfaces();
}
@@ -306,24 +305,6 @@ public class WindowAnimator {
}
}
- int getPendingLayoutChanges(final int displayId) {
- if (displayId < 0) {
- return 0;
- }
- final DisplayContent displayContent = mService.mRoot.getDisplayContent(displayId);
- return (displayContent != null) ? displayContent.pendingLayoutChanges : 0;
- }
-
- void setPendingLayoutChanges(final int displayId, final int changes) {
- if (displayId < 0) {
- return;
- }
- final DisplayContent displayContent = mService.mRoot.getDisplayContent(displayId);
- if (displayContent != null) {
- displayContent.pendingLayoutChanges |= changes;
- }
- }
-
private DisplayContentsAnimator getDisplayContentsAnimatorLocked(int displayId) {
if (displayId < 0) {
return null;
diff --git a/services/core/java/com/android/server/wm/WindowChangeAnimationSpec.java b/services/core/java/com/android/server/wm/WindowChangeAnimationSpec.java
index 775d5b2fb79a..d5d4e085b7d2 100644
--- a/services/core/java/com/android/server/wm/WindowChangeAnimationSpec.java
+++ b/services/core/java/com/android/server/wm/WindowChangeAnimationSpec.java
@@ -70,11 +70,6 @@ public class WindowChangeAnimationSpec implements AnimationSpec {
}
@Override
- public int getBackgroundColor() {
- return 0;
- }
-
- @Override
public long getDuration() {
return mAnimation.getDuration();
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index e280a663b7f5..586375f9d714 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -895,6 +895,12 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
}
+ void forAllWallpaperWindows(Consumer<WallpaperWindowToken> callback) {
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ mChildren.get(i).forAllWallpaperWindows(callback);
+ }
+ }
+
/**
* For all tasks at or below this container call the callback.
*
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 8d3a107c2f25..14214b4be7d1 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
@@ -314,11 +313,6 @@ public class WindowManagerService extends IWindowManager.Stub
static final int WINDOW_LAYER_MULTIPLIER = 5;
/**
- * Dim surface layer is immediately below target window.
- */
- static final int LAYER_OFFSET_DIM = 1;
-
- /**
* Animation thumbnail is as far as possible below the window above
* the thumbnail (or in other words as far as possible above the window
* below it).
@@ -368,14 +362,28 @@ public class WindowManagerService extends IWindowManager.Stub
private static final String DENSITY_OVERRIDE = "ro.config.density_override";
private static final String SIZE_OVERRIDE = "ro.config.size_override";
- private static final int MAX_SCREENSHOT_RETRIES = 3;
-
private static final String PROPERTY_EMULATOR_CIRCULAR = "ro.emulator.circular";
// Used to indicate that if there is already a transition set, it should be preserved when
// trying to apply a new one.
private static final boolean ALWAYS_KEEP_CURRENT = true;
+ /**
+ * If set, new app transition framework which supports setting animation on any element
+ * in a surface is used.
+ * <p>
+ * Only set this to non-zero once the new app transition framework is productionalized.
+ * </p>
+ */
+ private static final String HIERARCHICAL_ANIMATIONS_PROPERTY =
+ "persist.wm.hierarchical_animations";
+
+ /**
+ * @see #HIERARCHICAL_ANIMATIONS_PROPERTY
+ */
+ static boolean sHierarchicalAnimations =
+ SystemProperties.getBoolean(HIERARCHICAL_ANIMATIONS_PROPERTY, false);
+
// Enums for animation scale update types.
@Retention(RetentionPolicy.SOURCE)
@IntDef({WINDOW_ANIMATION_SCALE, TRANSITION_ANIMATION_SCALE, ANIMATION_DURATION_SCALE})
@@ -1217,7 +1225,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 +1680,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();
@@ -2046,13 +2057,11 @@ public class WindowManagerService extends IWindowManager.Stub
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
long origId = Binder.clearCallingIdentity();
- final int displayId;
synchronized (mGlobalLock) {
final WindowState win = windowForClientLocked(session, client, false);
if (win == null) {
return 0;
}
- displayId = win.getDisplayId();
final DisplayContent displayContent = win.getDisplayContent();
final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
@@ -2339,16 +2348,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;
@@ -2628,16 +2639,14 @@ public class WindowManagerService extends IWindowManager.Stub
getDefaultDisplayContentLocked().executeAppTransition();
}
- public void initializeRecentsAnimation(int targetActivityType,
+ void initializeRecentsAnimation(int targetActivityType,
IRecentsAnimationRunner recentsAnimationRunner,
RecentsAnimationController.RecentsAnimationCallbacks callbacks, int displayId,
SparseBooleanArray recentTaskIds) {
- synchronized (mGlobalLock) {
- mRecentsAnimationController = new RecentsAnimationController(this,
- recentsAnimationRunner, callbacks, displayId);
- mRoot.getDisplayContent(displayId).mAppTransition.updateBooster();
- mRecentsAnimationController.initialize(targetActivityType, recentTaskIds);
- }
+ mRecentsAnimationController = new RecentsAnimationController(this, recentsAnimationRunner,
+ callbacks, displayId);
+ mRoot.getDisplayContent(displayId).mAppTransition.updateBooster();
+ mRecentsAnimationController.initialize(targetActivityType, recentTaskIds);
}
@VisibleForTesting
@@ -2775,21 +2784,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
@@ -2798,12 +2792,6 @@ public class WindowManagerService extends IWindowManager.Stub
mAtmInternal.notifyKeyguardFlagsChanged(callback, displayId);
}
- public boolean isKeyguardTrusted() {
- synchronized (mGlobalLock) {
- return mPolicy.isKeyguardTrustedLw();
- }
- }
-
public void setKeyguardGoingAway(boolean keyguardGoingAway) {
synchronized (mGlobalLock) {
mKeyguardGoingAway = keyguardGoingAway;
@@ -2949,13 +2937,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- public boolean isShowingDream() {
- synchronized (mGlobalLock) {
- // TODO(b/123372519): Fix this when dream can be shown on non-default display.
- return getDefaultDisplayContentLocked().getDisplayPolicy().isShowingDreamLw();
- }
- }
-
@Override
public void dismissKeyguard(IKeyguardDismissCallback callback, CharSequence message) {
if (!checkCallingPermission(permission.CONTROL_KEYGUARD, "dismissKeyguard")) {
@@ -2966,12 +2947,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- public void onKeyguardOccludedChanged(boolean occluded) {
- synchronized (mGlobalLock) {
- mPolicy.onKeyguardOccludedChangedLw(occluded);
- }
- }
-
@Override
public void setSwitchingUser(boolean switching) {
if (!checkCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL,
@@ -5242,7 +5217,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(
@@ -5342,9 +5317,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
void requestTraversal() {
- synchronized (mGlobalLock) {
- mWindowPlacerLocked.requestTraversal();
- }
+ mWindowPlacerLocked.requestTraversal();
}
/** Note that Locked in this case is on mLayoutToAnim */
@@ -5812,55 +5785,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- public void notifyAppRelaunching(IBinder token) {
- synchronized (mGlobalLock) {
- final AppWindowToken appWindow = mRoot.getAppWindowToken(token);
- if (appWindow != null) {
- appWindow.startRelaunching();
- }
- }
- }
-
- public void notifyAppRelaunchingFinished(IBinder token) {
- synchronized (mGlobalLock) {
- final AppWindowToken appWindow = mRoot.getAppWindowToken(token);
- if (appWindow != null) {
- appWindow.finishRelaunching();
- }
- }
- }
-
- public void notifyAppRelaunchesCleared(IBinder token) {
- synchronized (mGlobalLock) {
- final AppWindowToken appWindow = mRoot.getAppWindowToken(token);
- if (appWindow != null) {
- appWindow.clearRelaunching();
- }
- }
- }
-
- public void notifyAppResumedFinished(IBinder token) {
- synchronized (mGlobalLock) {
- final AppWindowToken appWindow = mRoot.getAppWindowToken(token);
- if (appWindow != null) {
- appWindow.getDisplayContent().mUnknownAppVisibilityController
- .notifyAppResumedFinished(appWindow);
- }
- }
- }
-
- /**
- * Called when a task has been removed from the recent tasks list.
- * <p>
- * Note: This doesn't go through {@link TaskWindowContainerController} yet as the window
- * container may not exist when this happens.
- */
- public void notifyTaskRemovedFromRecents(int taskId, int userId) {
- synchronized (mGlobalLock) {
- mTaskSnapshotController.notifyTaskRemovedFromRecents(taskId, userId);
- }
- }
-
private void dumpPolicyLocked(PrintWriter pw, String[] args, boolean dumpAll) {
pw.println("WINDOW MANAGER POLICY STATE (dumpsys window policy)");
mPolicy.dump(" ", pw, args);
@@ -6370,16 +6294,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- public void onDisplayChanged(int displayId) {
- synchronized (mGlobalLock) {
- final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
- if (displayContent != null) {
- displayContent.updateDisplayInfo();
- }
- mWindowPlacerLocked.requestTraversal();
- }
- }
-
@Override
public Object getWindowManagerLock() {
return mGlobalLock;
@@ -6390,21 +6304,18 @@ public class WindowManagerService extends IWindowManager.Stub
* a window.
* @param token Application token for which the activity will be relaunched.
*/
- public void setWillReplaceWindow(IBinder token, boolean animate) {
- synchronized (mGlobalLock) {
- final AppWindowToken appWindowToken = mRoot.getAppWindowToken(token);
- if (appWindowToken == null) {
- Slog.w(TAG_WM, "Attempted to set replacing window on non-existing app token "
- + token);
- return;
- }
- if (!appWindowToken.hasContentToDisplay()) {
- Slog.w(TAG_WM, "Attempted to set replacing window on app token with no content"
- + token);
- return;
- }
- appWindowToken.setWillReplaceWindows(animate);
+ void setWillReplaceWindow(IBinder token, boolean animate) {
+ final AppWindowToken appWindowToken = mRoot.getAppWindowToken(token);
+ if (appWindowToken == null) {
+ Slog.w(TAG_WM, "Attempted to set replacing window on non-existing app token " + token);
+ return;
+ }
+ if (!appWindowToken.hasContentToDisplay()) {
+ Slog.w(TAG_WM, "Attempted to set replacing window on app token with no content"
+ + token);
+ return;
}
+ appWindowToken.setWillReplaceWindows(animate);
}
/**
@@ -6452,19 +6363,17 @@ public class WindowManagerService extends IWindowManager.Stub
* @param token Application token for the activity whose window might be replaced.
* @param replacing Whether the window is being replaced or not.
*/
- public void scheduleClearWillReplaceWindows(IBinder token, boolean replacing) {
- synchronized (mGlobalLock) {
- final AppWindowToken appWindowToken = mRoot.getAppWindowToken(token);
- if (appWindowToken == null) {
- Slog.w(TAG_WM, "Attempted to reset replacing window on non-existing app token "
- + token);
- return;
- }
- if (replacing) {
- scheduleWindowReplacementTimeouts(appWindowToken);
- } else {
- appWindowToken.clearWillReplaceWindows();
- }
+ void scheduleClearWillReplaceWindows(IBinder token, boolean replacing) {
+ final AppWindowToken appWindowToken = mRoot.getAppWindowToken(token);
+ if (appWindowToken == null) {
+ Slog.w(TAG_WM, "Attempted to reset replacing window on non-existing app token "
+ + token);
+ return;
+ }
+ if (replacing) {
+ scheduleWindowReplacementTimeouts(appWindowToken);
+ } else {
+ appWindowToken.clearWillReplaceWindows();
}
}
@@ -6486,11 +6395,9 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- public void setDockedStackResizing(boolean resizing) {
- synchronized (mGlobalLock) {
- getDefaultDisplayContentLocked().getDockedDividerController().setResizing(resizing);
- requestTraversal();
- }
+ void setDockedStackResizing(boolean resizing) {
+ getDefaultDisplayContentLocked().getDockedDividerController().setResizing(resizing);
+ requestTraversal();
}
@Override
@@ -7035,7 +6942,9 @@ public class WindowManagerService extends IWindowManager.Stub
private final class LocalService extends WindowManagerInternal {
@Override
public void requestTraversalFromDisplayManager() {
- requestTraversal();
+ synchronized (mGlobalLock) {
+ requestTraversal();
+ }
}
@Override
@@ -7760,4 +7669,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/WindowProcessListener.java b/services/core/java/com/android/server/wm/WindowProcessListener.java
index 23d7a6a9d293..1dade1519fdb 100644
--- a/services/core/java/com/android/server/wm/WindowProcessListener.java
+++ b/services/core/java/com/android/server/wm/WindowProcessListener.java
@@ -17,7 +17,6 @@
package com.android.server.wm;
import android.util.proto.ProtoOutputStream;
-import android.view.IRemoteAnimationRunner;
import android.view.RemoteAnimationAdapter;
/**
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index cbb0b3aab687..501a93ef6645 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;
@@ -520,11 +520,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
/** When true this window can be displayed on screens owther than mOwnerUid's */
private boolean mShowToOwnerOnly;
- // Whether the window was visible when we set the app to invisible last time. WM uses
- // this as a hint to restore the surface (if available) for early animation next time
- // the app is brought visible.
- private boolean mWasVisibleBeforeClientHidden;
-
// This window will be replaced due to relaunch. This allows window manager
// to differentiate between simple removal of a window and replacement. In the latter case it
// will preserve the old window until the new one is drawn.
@@ -755,9 +750,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 +821,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 +1121,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 +1277,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 +1978,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();
@@ -2008,8 +2008,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// Visibility of the removed window. Will be used later to update orientation later on.
boolean wasVisible = false;
- final int displayId = getDisplayId();
-
// First, see if we need to run an animation. If we do, we have to hold off on removing the
// window until the animation is done. If the display is frozen, just remove immediately,
// since the animation wouldn't be seen.
@@ -4377,8 +4375,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..c676e723de71 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;
@@ -270,20 +271,17 @@ class WindowStateAnimator {
if (mAttrType == LayoutParams.TYPE_STATUS_BAR && mWin.isVisibleByPolicy()) {
// Upon completion of a not-visible to visible status bar animation a relayout is
// required.
- if (displayContent != null) {
- displayContent.setLayoutNeeded();
- }
+ displayContent.setLayoutNeeded();
}
mWin.onExitAnimationDone();
- final int displayId = mWin.getDisplayId();
- int pendingLayoutChanges = FINISH_LAYOUT_REDO_ANIM;
+ displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_ANIM;
if (displayContent.mWallpaperController.isWallpaperTarget(mWin)) {
- pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
+ displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
- mAnimator.setPendingLayoutChanges(displayId, pendingLayoutChanges);
- if (DEBUG_LAYOUT_REPEATS)
+ if (DEBUG_LAYOUT_REPEATS) {
mService.mWindowPlacerLocked.debugLayoutRepeats(
- "WindowStateAnimator", mAnimator.getPendingLayoutChanges(displayId));
+ "WindowStateAnimator", displayContent.pendingLayoutChanges);
+ }
if (mWin.mAppToken != null) {
mWin.mAppToken.updateReportedVisibilityLocked();
@@ -428,10 +426,6 @@ class WindowStateAnimator {
}
}
- private int getLayerStack() {
- return mWin.getDisplayContent().getDisplay().getLayerStack();
- }
-
void resetDrawState() {
mDrawState = DRAW_PENDING;
@@ -540,8 +534,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 +548,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 +741,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 +758,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;
@@ -1068,8 +1065,7 @@ class WindowStateAnimator {
if (mSurfaceResized) {
mReportSurfaceResized = true;
- mAnimator.setPendingLayoutChanges(w.getDisplayId(),
- FINISH_LAYOUT_REDO_WALLPAPER);
+ mWin.getDisplayContent().pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
}
@@ -1164,16 +1160,16 @@ class WindowStateAnimator {
if (mIsWallpaper) {
w.dispatchWallpaperVisibility(true);
}
- if (!w.getDisplayContent().getLastHasContent()) {
+ final DisplayContent displayContent = w.getDisplayContent();
+ if (!displayContent.getLastHasContent()) {
// This draw means the difference between unique content and mirroring.
// Run another pass through performLayout to set mHasContent in the
// LogicalDisplay.
- mAnimator.setPendingLayoutChanges(w.getDisplayId(),
- FINISH_LAYOUT_REDO_ANIM);
+ displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_ANIM;
if (DEBUG_LAYOUT_REPEATS) {
mService.mWindowPlacerLocked.debugLayoutRepeats(
"showSurfaceRobustlyLocked " + w,
- mAnimator.getPendingLayoutChanges(w.getDisplayId()));
+ displayContent.pendingLayoutChanges);
}
}
} else {
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 97b20472b12d..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: [
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/core/jni/com_android_server_security_VerityUtils.cpp b/services/core/jni/com_android_server_security_VerityUtils.cpp
index 6cd9f2c718ee..9ceb7706628a 100644
--- a/services/core/jni/com_android_server_security_VerityUtils.cpp
+++ b/services/core/jni/com_android_server_security_VerityUtils.cpp
@@ -36,61 +36,33 @@
#include <linux/fsverity.h>
#else
-// Before fs-verity is upstreamed, use the current snapshot for development.
-// https://git.kernel.org/pub/scm/linux/kernel/git/ebiggers/linux.git/tree/include/uapi/linux/fsverity.h?h=fsverity
-
#include <linux/limits.h>
#include <linux/ioctl.h>
#include <linux/types.h>
+#define FS_VERITY_HASH_ALG_SHA256 1
+
+struct fsverity_enable_arg {
+ __u32 version;
+ __u32 hash_algorithm;
+ __u32 block_size;
+ __u32 salt_size;
+ __u64 salt_ptr;
+ __u32 sig_size;
+ __u32 __reserved1;
+ __u64 sig_ptr;
+ __u64 __reserved2[11];
+};
+
struct fsverity_digest {
__u16 digest_algorithm;
__u16 digest_size; /* input/output */
__u8 digest[];
};
-#define FS_IOC_ENABLE_VERITY _IO('f', 133)
+#define FS_IOC_ENABLE_VERITY _IOW('f', 133, struct fsverity_enable_arg)
#define FS_IOC_MEASURE_VERITY _IOWR('f', 134, struct fsverity_digest)
-#define FS_VERITY_MAGIC "FSVerity"
-
-#define FS_VERITY_ALG_SHA256 1
-
-struct fsverity_descriptor {
- __u8 magic[8]; /* must be FS_VERITY_MAGIC */
- __u8 major_version; /* must be 1 */
- __u8 minor_version; /* must be 0 */
- __u8 log_data_blocksize;/* log2(data-bytes-per-hash), e.g. 12 for 4KB */
- __u8 log_tree_blocksize;/* log2(tree-bytes-per-hash), e.g. 12 for 4KB */
- __le16 data_algorithm; /* hash algorithm for data blocks */
- __le16 tree_algorithm; /* hash algorithm for tree blocks */
- __le32 flags; /* flags */
- __le32 __reserved1; /* must be 0 */
- __le64 orig_file_size; /* size of the original file data */
- __le16 auth_ext_count; /* number of authenticated extensions */
- __u8 __reserved2[30]; /* must be 0 */
-};
-
-#define FS_VERITY_EXT_ROOT_HASH 1
-#define FS_VERITY_EXT_PKCS7_SIGNATURE 3
-
-struct fsverity_extension {
- __le32 length;
- __le16 type; /* Type of this extension (see codes above) */
- __le16 __reserved; /* Reserved, must be 0 */
-};
-
-struct fsverity_digest_disk {
- __le16 digest_algorithm;
- __le16 digest_size;
- __u8 digest[];
-};
-
-struct fsverity_footer {
- __le32 desc_reverse_offset; /* distance to fsverity_descriptor */
- __u8 magic[8]; /* FS_VERITY_MAGIC */
-} __packed;
-
#endif
const int kSha256Bytes = 32;
@@ -99,52 +71,24 @@ namespace android {
namespace {
-class JavaByteArrayHolder {
- public:
- JavaByteArrayHolder(const JavaByteArrayHolder &other) = delete;
- JavaByteArrayHolder(JavaByteArrayHolder &&other)
- : mEnv(other.mEnv), mBytes(other.mBytes), mElements(other.mElements) {
- other.mElements = nullptr;
- }
-
- static JavaByteArrayHolder newArray(JNIEnv* env, jsize size) {
- return JavaByteArrayHolder(env, size);
- }
-
- jbyte* getRaw() {
- return mElements;
- }
-
- jbyteArray release() {
- mEnv->ReleaseByteArrayElements(mBytes, mElements, 0);
- mElements = nullptr;
- return mBytes;
- }
-
- ~JavaByteArrayHolder() {
- LOG_ALWAYS_FATAL_IF(mElements != nullptr, "Elements are not released");
- }
-
- private:
- JavaByteArrayHolder(JNIEnv* env, jsize size) {
- mEnv = env;
- mBytes = mEnv->NewByteArray(size);
- mElements = mEnv->GetByteArrayElements(mBytes, nullptr);
- memset(mElements, 0, size);
- }
-
- JNIEnv* mEnv;
- jbyteArray mBytes;
- jbyte* mElements;
-};
-
-int enableFsverity(JNIEnv* env, jobject /* clazz */, jstring filePath) {
+int enableFsverity(JNIEnv* env, jobject /* clazz */, jstring filePath, jbyteArray signature) {
const char* path = env->GetStringUTFChars(filePath, nullptr);
::android::base::unique_fd rfd(open(path, O_RDONLY | O_CLOEXEC));
+ env->ReleaseStringUTFChars(filePath, path);
if (rfd.get() < 0) {
return errno;
}
- if (ioctl(rfd.get(), FS_IOC_ENABLE_VERITY, nullptr) < 0) {
+
+ fsverity_enable_arg arg = {};
+ arg.version = 1;
+ arg.hash_algorithm = FS_VERITY_HASH_ALG_SHA256;
+ arg.block_size = 4096;
+ arg.salt_size = 0;
+ arg.salt_ptr = reinterpret_cast<uintptr_t>(nullptr);
+ arg.sig_size = env->GetArrayLength(signature);
+ arg.sig_ptr = reinterpret_cast<uintptr_t>(signature);
+
+ if (ioctl(rfd.get(), FS_IOC_ENABLE_VERITY, &arg) < 0) {
return errno;
}
return 0;
@@ -159,6 +103,7 @@ int measureFsverity(JNIEnv* env, jobject /* clazz */, jstring filePath) {
const char* path = env->GetStringUTFChars(filePath, nullptr);
::android::base::unique_fd rfd(open(path, O_RDONLY | O_CLOEXEC));
+ env->ReleaseStringUTFChars(filePath, path);
if (rfd.get() < 0) {
return errno;
}
@@ -168,71 +113,9 @@ int measureFsverity(JNIEnv* env, jobject /* clazz */, jstring filePath) {
return 0;
}
-jbyteArray constructFsveritySignedData(JNIEnv* env, jobject /* clazz */, jbyteArray digest) {
- auto raii = JavaByteArrayHolder::newArray(env, sizeof(fsverity_digest_disk) + kSha256Bytes);
- fsverity_digest_disk* data = reinterpret_cast<fsverity_digest_disk*>(raii.getRaw());
-
- data->digest_algorithm = FS_VERITY_ALG_SHA256;
- data->digest_size = kSha256Bytes;
- if (env->GetArrayLength(digest) != kSha256Bytes) {
- jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", "Invalid hash size of %d",
- env->GetArrayLength(digest));
- return 0;
- }
- const jbyte* src = env->GetByteArrayElements(digest, nullptr);
- memcpy(data->digest, src, kSha256Bytes);
-
- return raii.release();
-}
-
-
-jbyteArray constructFsverityDescriptor(JNIEnv* env, jobject /* clazz */, jlong fileSize) {
- auto raii = JavaByteArrayHolder::newArray(env, sizeof(fsverity_descriptor));
- fsverity_descriptor* desc = reinterpret_cast<fsverity_descriptor*>(raii.getRaw());
-
- memcpy(desc->magic, FS_VERITY_MAGIC, sizeof(desc->magic));
- desc->major_version = 1;
- desc->minor_version = 0;
- desc->log_data_blocksize = 12;
- desc->log_tree_blocksize = 12;
- desc->data_algorithm = FS_VERITY_ALG_SHA256;
- desc->tree_algorithm = FS_VERITY_ALG_SHA256;
- desc->flags = 0;
- desc->orig_file_size = fileSize;
- desc->auth_ext_count = 1;
-
- return raii.release();
-}
-
-jbyteArray constructFsverityExtension(JNIEnv* env, jobject /* clazz */, jshort extensionId,
- jint extensionDataSize) {
- auto raii = JavaByteArrayHolder::newArray(env, sizeof(fsverity_extension));
- fsverity_extension* ext = reinterpret_cast<fsverity_extension*>(raii.getRaw());
-
- ext->length = sizeof(fsverity_extension) + extensionDataSize;
- ext->type = extensionId;
-
- return raii.release();
-}
-
-jbyteArray constructFsverityFooter(JNIEnv* env, jobject /* clazz */,
- jint offsetToDescriptorHead) {
- auto raii = JavaByteArrayHolder::newArray(env, sizeof(fsverity_footer));
- fsverity_footer* footer = reinterpret_cast<fsverity_footer*>(raii.getRaw());
-
- footer->desc_reverse_offset = offsetToDescriptorHead + sizeof(fsverity_footer);
- memcpy(footer->magic, FS_VERITY_MAGIC, sizeof(footer->magic));
-
- return raii.release();
-}
-
const JNINativeMethod sMethods[] = {
- { "enableFsverityNative", "(Ljava/lang/String;)I", (void *)enableFsverity },
+ { "enableFsverityNative", "(Ljava/lang/String;[B)I", (void *)enableFsverity },
{ "measureFsverityNative", "(Ljava/lang/String;)I", (void *)measureFsverity },
- { "constructFsveritySignedDataNative", "([B)[B", (void *)constructFsveritySignedData },
- { "constructFsverityDescriptorNative", "(J)[B", (void *)constructFsverityDescriptor },
- { "constructFsverityExtensionNative", "(SI)[B", (void *)constructFsverityExtension },
- { "constructFsverityFooterNative", "(I)[B", (void *)constructFsverityFooter },
};
} // namespace
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 478bc88fe815..704c80870fe5 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1113,142 +1113,107 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
info.writePoliciesToXml(out);
out.endTag(null, TAG_POLICIES);
if (minimumPasswordMetrics.quality != PASSWORD_QUALITY_UNSPECIFIED) {
- out.startTag(null, TAG_PASSWORD_QUALITY);
- out.attribute(null, ATTR_VALUE, Integer.toString(minimumPasswordMetrics.quality));
- out.endTag(null, TAG_PASSWORD_QUALITY);
+ writeAttributeValueToXml(
+ out, TAG_PASSWORD_QUALITY, minimumPasswordMetrics.quality);
if (minimumPasswordMetrics.length != DEF_MINIMUM_PASSWORD_LENGTH) {
- out.startTag(null, TAG_MIN_PASSWORD_LENGTH);
- out.attribute(
- null, ATTR_VALUE, Integer.toString(minimumPasswordMetrics.length));
- out.endTag(null, TAG_MIN_PASSWORD_LENGTH);
- }
- if(passwordHistoryLength != DEF_PASSWORD_HISTORY_LENGTH) {
- out.startTag(null, TAG_PASSWORD_HISTORY_LENGTH);
- out.attribute(null, ATTR_VALUE, Integer.toString(passwordHistoryLength));
- out.endTag(null, TAG_PASSWORD_HISTORY_LENGTH);
+ writeAttributeValueToXml(
+ out, TAG_MIN_PASSWORD_LENGTH, minimumPasswordMetrics.length);
}
if (minimumPasswordMetrics.upperCase != DEF_MINIMUM_PASSWORD_UPPER_CASE) {
- out.startTag(null, TAG_MIN_PASSWORD_UPPERCASE);
- out.attribute(
- null, ATTR_VALUE, Integer.toString(minimumPasswordMetrics.upperCase));
- out.endTag(null, TAG_MIN_PASSWORD_UPPERCASE);
+ writeAttributeValueToXml(
+ out, TAG_MIN_PASSWORD_UPPERCASE, minimumPasswordMetrics.upperCase);
}
if (minimumPasswordMetrics.lowerCase != DEF_MINIMUM_PASSWORD_LOWER_CASE) {
- out.startTag(null, TAG_MIN_PASSWORD_LOWERCASE);
- out.attribute(
- null, ATTR_VALUE, Integer.toString(minimumPasswordMetrics.lowerCase));
- out.endTag(null, TAG_MIN_PASSWORD_LOWERCASE);
+ writeAttributeValueToXml(
+ out, TAG_MIN_PASSWORD_LOWERCASE, minimumPasswordMetrics.lowerCase);
}
if (minimumPasswordMetrics.letters != DEF_MINIMUM_PASSWORD_LETTERS) {
- out.startTag(null, TAG_MIN_PASSWORD_LETTERS);
- out.attribute(
- null, ATTR_VALUE, Integer.toString(minimumPasswordMetrics.letters));
- out.endTag(null, TAG_MIN_PASSWORD_LETTERS);
+ writeAttributeValueToXml(
+ out, TAG_MIN_PASSWORD_LETTERS, minimumPasswordMetrics.letters);
}
if (minimumPasswordMetrics.numeric != DEF_MINIMUM_PASSWORD_NUMERIC) {
- out.startTag(null, TAG_MIN_PASSWORD_NUMERIC);
- out.attribute(
- null, ATTR_VALUE, Integer.toString(minimumPasswordMetrics.numeric));
- out.endTag(null, TAG_MIN_PASSWORD_NUMERIC);
+ writeAttributeValueToXml(
+ out, TAG_MIN_PASSWORD_NUMERIC, minimumPasswordMetrics.numeric);
}
if (minimumPasswordMetrics.symbols != DEF_MINIMUM_PASSWORD_SYMBOLS) {
- out.startTag(null, TAG_MIN_PASSWORD_SYMBOLS);
- out.attribute(
- null, ATTR_VALUE, Integer.toString(minimumPasswordMetrics.symbols));
- out.endTag(null, TAG_MIN_PASSWORD_SYMBOLS);
+ writeAttributeValueToXml(
+ out, TAG_MIN_PASSWORD_SYMBOLS, minimumPasswordMetrics.symbols);
}
if (minimumPasswordMetrics.nonLetter > DEF_MINIMUM_PASSWORD_NON_LETTER) {
- out.startTag(null, TAG_MIN_PASSWORD_NONLETTER);
- out.attribute(
- null, ATTR_VALUE, Integer.toString(minimumPasswordMetrics.nonLetter));
- out.endTag(null, TAG_MIN_PASSWORD_NONLETTER);
+ writeAttributeValueToXml(
+ out, TAG_MIN_PASSWORD_NONLETTER, minimumPasswordMetrics.nonLetter);
}
}
+ if (passwordHistoryLength != DEF_PASSWORD_HISTORY_LENGTH) {
+ writeAttributeValueToXml(
+ out, TAG_PASSWORD_HISTORY_LENGTH, passwordHistoryLength);
+ }
if (maximumTimeToUnlock != DEF_MAXIMUM_TIME_TO_UNLOCK) {
- out.startTag(null, TAG_MAX_TIME_TO_UNLOCK);
- out.attribute(null, ATTR_VALUE, Long.toString(maximumTimeToUnlock));
- out.endTag(null, TAG_MAX_TIME_TO_UNLOCK);
+ writeAttributeValueToXml(
+ out, TAG_MAX_TIME_TO_UNLOCK, maximumTimeToUnlock);
}
if (strongAuthUnlockTimeout != DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS) {
- out.startTag(null, TAG_STRONG_AUTH_UNLOCK_TIMEOUT);
- out.attribute(null, ATTR_VALUE, Long.toString(strongAuthUnlockTimeout));
- out.endTag(null, TAG_STRONG_AUTH_UNLOCK_TIMEOUT);
+ writeAttributeValueToXml(
+ out, TAG_STRONG_AUTH_UNLOCK_TIMEOUT, strongAuthUnlockTimeout);
}
if (maximumFailedPasswordsForWipe != DEF_MAXIMUM_FAILED_PASSWORDS_FOR_WIPE) {
- out.startTag(null, TAG_MAX_FAILED_PASSWORD_WIPE);
- out.attribute(null, ATTR_VALUE, Integer.toString(maximumFailedPasswordsForWipe));
- out.endTag(null, TAG_MAX_FAILED_PASSWORD_WIPE);
+ writeAttributeValueToXml(
+ out, TAG_MAX_FAILED_PASSWORD_WIPE, maximumFailedPasswordsForWipe);
}
if (specifiesGlobalProxy) {
- out.startTag(null, TAG_SPECIFIES_GLOBAL_PROXY);
- out.attribute(null, ATTR_VALUE, Boolean.toString(specifiesGlobalProxy));
- out.endTag(null, TAG_SPECIFIES_GLOBAL_PROXY);
+ writeAttributeValueToXml(
+ out, TAG_SPECIFIES_GLOBAL_PROXY, specifiesGlobalProxy);
if (globalProxySpec != null) {
- out.startTag(null, TAG_GLOBAL_PROXY_SPEC);
- out.attribute(null, ATTR_VALUE, globalProxySpec);
- out.endTag(null, TAG_GLOBAL_PROXY_SPEC);
+ writeAttributeValueToXml(out, TAG_GLOBAL_PROXY_SPEC, globalProxySpec);
}
if (globalProxyExclusionList != null) {
- out.startTag(null, TAG_GLOBAL_PROXY_EXCLUSION_LIST);
- out.attribute(null, ATTR_VALUE, globalProxyExclusionList);
- out.endTag(null, TAG_GLOBAL_PROXY_EXCLUSION_LIST);
+ writeAttributeValueToXml(
+ out, TAG_GLOBAL_PROXY_EXCLUSION_LIST, globalProxyExclusionList);
}
}
if (passwordExpirationTimeout != DEF_PASSWORD_EXPIRATION_TIMEOUT) {
- out.startTag(null, TAG_PASSWORD_EXPIRATION_TIMEOUT);
- out.attribute(null, ATTR_VALUE, Long.toString(passwordExpirationTimeout));
- out.endTag(null, TAG_PASSWORD_EXPIRATION_TIMEOUT);
+ writeAttributeValueToXml(
+ out, TAG_PASSWORD_EXPIRATION_TIMEOUT, passwordExpirationTimeout);
}
if (passwordExpirationDate != DEF_PASSWORD_EXPIRATION_DATE) {
- out.startTag(null, TAG_PASSWORD_EXPIRATION_DATE);
- out.attribute(null, ATTR_VALUE, Long.toString(passwordExpirationDate));
- out.endTag(null, TAG_PASSWORD_EXPIRATION_DATE);
+ writeAttributeValueToXml(
+ out, TAG_PASSWORD_EXPIRATION_DATE, passwordExpirationDate);
}
if (encryptionRequested) {
- out.startTag(null, TAG_ENCRYPTION_REQUESTED);
- out.attribute(null, ATTR_VALUE, Boolean.toString(encryptionRequested));
- out.endTag(null, TAG_ENCRYPTION_REQUESTED);
+ writeAttributeValueToXml(
+ out, TAG_ENCRYPTION_REQUESTED, encryptionRequested);
}
if (testOnlyAdmin) {
- out.startTag(null, TAG_TEST_ONLY_ADMIN);
- out.attribute(null, ATTR_VALUE, Boolean.toString(testOnlyAdmin));
- out.endTag(null, TAG_TEST_ONLY_ADMIN);
+ writeAttributeValueToXml(
+ out, TAG_TEST_ONLY_ADMIN, testOnlyAdmin);
}
if (disableCamera) {
- out.startTag(null, TAG_DISABLE_CAMERA);
- out.attribute(null, ATTR_VALUE, Boolean.toString(disableCamera));
- out.endTag(null, TAG_DISABLE_CAMERA);
+ writeAttributeValueToXml(
+ out, TAG_DISABLE_CAMERA, disableCamera);
}
if (disableCallerId) {
- out.startTag(null, TAG_DISABLE_CALLER_ID);
- out.attribute(null, ATTR_VALUE, Boolean.toString(disableCallerId));
- out.endTag(null, TAG_DISABLE_CALLER_ID);
+ writeAttributeValueToXml(
+ out, TAG_DISABLE_CALLER_ID, disableCallerId);
}
if (disableContactsSearch) {
- out.startTag(null, TAG_DISABLE_CONTACTS_SEARCH);
- out.attribute(null, ATTR_VALUE, Boolean.toString(disableContactsSearch));
- out.endTag(null, TAG_DISABLE_CONTACTS_SEARCH);
+ writeAttributeValueToXml(
+ out, TAG_DISABLE_CONTACTS_SEARCH, disableContactsSearch);
}
if (!disableBluetoothContactSharing) {
- out.startTag(null, TAG_DISABLE_BLUETOOTH_CONTACT_SHARING);
- out.attribute(null, ATTR_VALUE,
- Boolean.toString(disableBluetoothContactSharing));
- out.endTag(null, TAG_DISABLE_BLUETOOTH_CONTACT_SHARING);
+ writeAttributeValueToXml(
+ out, TAG_DISABLE_BLUETOOTH_CONTACT_SHARING, disableBluetoothContactSharing);
}
if (disableScreenCapture) {
- out.startTag(null, TAG_DISABLE_SCREEN_CAPTURE);
- out.attribute(null, ATTR_VALUE, Boolean.toString(disableScreenCapture));
- out.endTag(null, TAG_DISABLE_SCREEN_CAPTURE);
+ writeAttributeValueToXml(
+ out, TAG_DISABLE_SCREEN_CAPTURE, disableScreenCapture);
}
if (requireAutoTime) {
- out.startTag(null, TAG_REQUIRE_AUTO_TIME);
- out.attribute(null, ATTR_VALUE, Boolean.toString(requireAutoTime));
- out.endTag(null, TAG_REQUIRE_AUTO_TIME);
+ writeAttributeValueToXml(
+ out, TAG_REQUIRE_AUTO_TIME, requireAutoTime);
}
if (forceEphemeralUsers) {
- out.startTag(null, TAG_FORCE_EPHEMERAL_USERS);
- out.attribute(null, ATTR_VALUE, Boolean.toString(forceEphemeralUsers));
- out.endTag(null, TAG_FORCE_EPHEMERAL_USERS);
+ writeAttributeValueToXml(
+ out, TAG_FORCE_EPHEMERAL_USERS, forceEphemeralUsers);
}
if (isNetworkLoggingEnabled) {
out.startTag(null, TAG_IS_NETWORK_LOGGING_ENABLED);
@@ -1260,15 +1225,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
out.endTag(null, TAG_IS_NETWORK_LOGGING_ENABLED);
}
if (disabledKeyguardFeatures != DEF_KEYGUARD_FEATURES_DISABLED) {
- out.startTag(null, TAG_DISABLE_KEYGUARD_FEATURES);
- out.attribute(null, ATTR_VALUE, Integer.toString(disabledKeyguardFeatures));
- out.endTag(null, TAG_DISABLE_KEYGUARD_FEATURES);
+ writeAttributeValueToXml(
+ out, TAG_DISABLE_KEYGUARD_FEATURES, disabledKeyguardFeatures);
}
if (!accountTypesWithManagementDisabled.isEmpty()) {
- out.startTag(null, TAG_DISABLE_ACCOUNT_MANAGEMENT);
writeAttributeValuesToXml(
- out, TAG_ACCOUNT_TYPE, accountTypesWithManagementDisabled);
- out.endTag(null, TAG_DISABLE_ACCOUNT_MANAGEMENT);
+ out, TAG_DISABLE_ACCOUNT_MANAGEMENT, TAG_ACCOUNT_TYPE,
+ accountTypesWithManagementDisabled);
}
if (!trustAgentInfos.isEmpty()) {
Set<Entry<String, TrustAgentInfo>> set = trustAgentInfos.entrySet();
@@ -1291,9 +1254,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
out.endTag(null, TAG_MANAGE_TRUST_AGENT_FEATURES);
}
if (crossProfileWidgetProviders != null && !crossProfileWidgetProviders.isEmpty()) {
- out.startTag(null, TAG_CROSS_PROFILE_WIDGET_PROVIDERS);
- writeAttributeValuesToXml(out, TAG_PROVIDER, crossProfileWidgetProviders);
- out.endTag(null, TAG_CROSS_PROFILE_WIDGET_PROVIDERS);
+ writeAttributeValuesToXml(
+ out, TAG_CROSS_PROFILE_WIDGET_PROVIDERS, TAG_PROVIDER,
+ crossProfileWidgetProviders);
}
writePackageListToXml(out, TAG_PERMITTED_ACCESSIBILITY_SERVICES,
permittedAccessiblityServices);
@@ -1307,20 +1270,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
out, userRestrictions, TAG_USER_RESTRICTIONS);
}
if (!defaultEnabledRestrictionsAlreadySet.isEmpty()) {
- out.startTag(null, TAG_DEFAULT_ENABLED_USER_RESTRICTIONS);
- writeAttributeValuesToXml(
- out, TAG_RESTRICTION, defaultEnabledRestrictionsAlreadySet);
- out.endTag(null, TAG_DEFAULT_ENABLED_USER_RESTRICTIONS);
+ writeAttributeValuesToXml(out, TAG_DEFAULT_ENABLED_USER_RESTRICTIONS,
+ TAG_RESTRICTION,
+ defaultEnabledRestrictionsAlreadySet);
}
if (!TextUtils.isEmpty(shortSupportMessage)) {
- out.startTag(null, TAG_SHORT_SUPPORT_MESSAGE);
- out.text(shortSupportMessage.toString());
- out.endTag(null, TAG_SHORT_SUPPORT_MESSAGE);
+ writeTextToXml(out, TAG_SHORT_SUPPORT_MESSAGE, shortSupportMessage.toString());
}
if (!TextUtils.isEmpty(longSupportMessage)) {
- out.startTag(null, TAG_LONG_SUPPORT_MESSAGE);
- out.text(longSupportMessage.toString());
- out.endTag(null, TAG_LONG_SUPPORT_MESSAGE);
+ writeTextToXml(out, TAG_LONG_SUPPORT_MESSAGE, longSupportMessage.toString());
}
if (parentAdmin != null) {
out.startTag(null, TAG_PARENT_ADMIN);
@@ -1328,29 +1286,20 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
out.endTag(null, TAG_PARENT_ADMIN);
}
if (organizationColor != DEF_ORGANIZATION_COLOR) {
- out.startTag(null, TAG_ORGANIZATION_COLOR);
- out.attribute(null, ATTR_VALUE, Integer.toString(organizationColor));
- out.endTag(null, TAG_ORGANIZATION_COLOR);
+ writeAttributeValueToXml(out, TAG_ORGANIZATION_COLOR, organizationColor);
}
if (organizationName != null) {
- out.startTag(null, TAG_ORGANIZATION_NAME);
- out.text(organizationName);
- out.endTag(null, TAG_ORGANIZATION_NAME);
+ writeTextToXml(out, TAG_ORGANIZATION_NAME, organizationName);
}
if (isLogoutEnabled) {
- out.startTag(null, TAG_IS_LOGOUT_ENABLED);
- out.attribute(null, ATTR_VALUE, Boolean.toString(isLogoutEnabled));
- out.endTag(null, TAG_IS_LOGOUT_ENABLED);
+ writeAttributeValueToXml(
+ out, TAG_IS_LOGOUT_ENABLED, isLogoutEnabled);
}
if (startUserSessionMessage != null) {
- out.startTag(null, TAG_START_USER_SESSION_MESSAGE);
- out.text(startUserSessionMessage);
- out.endTag(null, TAG_START_USER_SESSION_MESSAGE);
+ writeTextToXml(out, TAG_START_USER_SESSION_MESSAGE, startUserSessionMessage);
}
if (endUserSessionMessage != null) {
- out.startTag(null, TAG_END_USER_SESSION_MESSAGE);
- out.text(endUserSessionMessage);
- out.endTag(null, TAG_END_USER_SESSION_MESSAGE);
+ writeTextToXml(out, TAG_END_USER_SESSION_MESSAGE, endUserSessionMessage);
}
if (mCrossProfileCalendarPackages == null) {
out.startTag(null, TAG_CROSS_PROFILE_CALENDAR_PACKAGES_NULL);
@@ -1361,25 +1310,58 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
+ void writeTextToXml(XmlSerializer out, String tag, String text) throws IOException {
+ out.startTag(null, tag);
+ out.text(text);
+ out.endTag(null, tag);
+ }
+
void writePackageListToXml(XmlSerializer out, String outerTag,
List<String> packageList)
throws IllegalArgumentException, IllegalStateException, IOException {
if (packageList == null) {
return;
}
+ writeAttributeValuesToXml(out, outerTag, TAG_PACKAGE_LIST_ITEM, packageList);
+ }
- out.startTag(null, outerTag);
- writeAttributeValuesToXml(out, TAG_PACKAGE_LIST_ITEM, packageList);
- out.endTag(null, outerTag);
+ void writeAttributeValueToXml(XmlSerializer out, String tag, String value)
+ throws IOException {
+ out.startTag(null, tag);
+ out.attribute(null, ATTR_VALUE, value);
+ out.endTag(null, tag);
}
- void writeAttributeValuesToXml(XmlSerializer out, String tag,
+ void writeAttributeValueToXml(XmlSerializer out, String tag, int value)
+ throws IOException {
+ out.startTag(null, tag);
+ out.attribute(null, ATTR_VALUE, Integer.toString(value));
+ out.endTag(null, tag);
+ }
+
+ void writeAttributeValueToXml(XmlSerializer out, String tag, long value)
+ throws IOException {
+ out.startTag(null, tag);
+ out.attribute(null, ATTR_VALUE, Long.toString(value));
+ out.endTag(null, tag);
+ }
+
+ void writeAttributeValueToXml(XmlSerializer out, String tag, boolean value)
+ throws IOException {
+ out.startTag(null, tag);
+ out.attribute(null, ATTR_VALUE, Boolean.toString(value));
+ out.endTag(null, tag);
+ }
+
+ void writeAttributeValuesToXml(XmlSerializer out, String outerTag, String innerTag,
@NonNull Collection<String> values) throws IOException {
+ out.startTag(null, outerTag);
for (String value : values) {
- out.startTag(null, tag);
+ out.startTag(null, innerTag);
out.attribute(null, ATTR_VALUE, value);
- out.endTag(null, tag);
+ out.endTag(null, innerTag);
}
+ out.endTag(null, outerTag);
}
void readFromXml(XmlPullParser parser, boolean shouldOverridePolicies)
@@ -1999,6 +1981,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return LocalServices.getService(LockSettingsInternal.class);
}
+ boolean hasUserSetupCompleted(DevicePolicyData userData) {
+ return userData.mUserSetupComplete;
+ }
+
boolean isBuildDebuggable() {
return Build.IS_DEBUGGABLE;
}
@@ -5659,7 +5645,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
KeyChain.bindAsUser(mContext, UserHandle.getUserHandleForUid(callingUid));
try {
IKeyChainService keyChain = keyChainConnection.getService();
- if (!keyChain.installKeyPair(privKey, cert, chain, alias)) {
+ if (!keyChain.installKeyPair(privKey, cert, chain, alias, KeyStore.UID_SELF)) {
return false;
}
if (requestAccess) {
@@ -8271,7 +8257,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/gestures/AccessibilityGestureDetectorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/gestures/AccessibilityGestureDetectorTest.java
index 2585a2832094..b7079124fb79 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/gestures/AccessibilityGestureDetectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/gestures/AccessibilityGestureDetectorTest.java
@@ -176,6 +176,6 @@ public class AccessibilityGestureDetectorTest {
// Check that correct gesture was recognized.
verify(mResultListener).onGestureCompleted(
- argThat(gestureInfo -> gestureInfo.getGestureId() == gestureId));
+ argThat(gestureEvent -> gestureEvent.getGestureId() == gestureId));
}
}
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 5ab44a8c2c3b..9e3b54d1ca96 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,14 @@ 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 +176,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);
@@ -301,18 +229,6 @@ public class MemoryStatUtilTest {
}
@Test
- public void testParseVmHWMFromProcfs_parsesCorrectValue() {
- assertEquals(137668, parseVmHWMFromProcfs(PROC_STATUS_CONTENTS));
- }
-
- @Test
- public void testParseVmHWMFromProcfs_emptyContents() {
- assertEquals(0, parseVmHWMFromProcfs(""));
-
- assertEquals(0, parseVmHWMFromProcfs(null));
- }
-
- @Test
public void testParseCmdlineFromProcfs_invalidValue() {
byte[] nothing = new byte[] {0x00, 0x74, 0x65, 0x73, 0x74}; // \0test
@@ -348,65 +264,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/audio/AudioDeviceBrokerTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
index 5c2ad94720f0..29a8dada7d89 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
@@ -126,6 +126,22 @@ public class AudioDeviceBrokerTest {
doTestConnectionDisconnectionReconnection(AudioService.BECOMING_NOISY_DELAY_MS / 2);
}
+ /**
+ * Verify connecting an A2DP sink will call into AudioService to unmute media
+ */
+ @Test
+ public void testA2dpConnectionUnmutesMedia() throws Exception {
+ Log.i(TAG, "testA2dpConnectionUnmutesMedia");
+ Assert.assertNotNull("invalid null BT device", mFakeBtDevice);
+
+ mAudioDeviceBroker.postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(mFakeBtDevice,
+ BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP, true, 1);
+ Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS);
+ verify(mMockAudioService, times(1)).postAccessoryPlugMediaUnmute(
+ ArgumentMatchers.eq(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP));
+
+ }
+
private void doTestConnectionDisconnectionReconnection(int delayAfterDisconnection)
throws Exception {
when(mMockAudioService.getDeviceForStream(AudioManager.STREAM_MUSIC))
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index d6cb9826d514..d90091017116 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -765,7 +765,38 @@ public class DevicePolicyManagerTest extends DpmTestBase {
}
/**
- * Test for: @{link DevicePolicyManager#reportPasswordChanged}
+ * Test for: {@link DevicePolicyManager#setPasswordHistoryLength(ComponentName, int)}
+ *
+ * Validates that when the password history length is set, it is persisted after rebooting
+ */
+ public void testSaveAndLoadPasswordHistoryLength_persistedAfterReboot() throws Exception {
+ int passwordHistoryLength = 2;
+
+ mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS);
+ mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+
+ // Install admin1 on system user.
+ setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
+
+ // Set admin1 to active admin and device owner
+ dpm.setActiveAdmin(admin1, false);
+ dpm.setDeviceOwner(admin1, null, UserHandle.USER_SYSTEM);
+
+ // Save password history length
+ dpm.setPasswordHistoryLength(admin1, passwordHistoryLength);
+
+ assertEquals(dpm.getPasswordHistoryLength(admin1), passwordHistoryLength);
+
+ initializeDpms();
+ reset(mContext.spiedContext);
+
+ // Password history length should persist after rebooted
+ assertEquals(dpm.getPasswordHistoryLength(admin1), passwordHistoryLength);
+ }
+
+ /**
+ * Test for: {@link DevicePolicyManager#reportPasswordChanged}
*
* Validates that when the password for a user changes, the notification broadcast intent
* {@link DeviceAdminReceiver#ACTION_PASSWORD_CHANGED} is sent to managed profile owners, in
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/PackageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
index fc7cfec9dc87..0a310d193675 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
@@ -118,17 +118,20 @@ public class PackageManagerServiceTest {
String[] partitions = { "system", "vendor", "odm", "oem", "product", "system_ext" };
String[] appdir = { "app", "priv-app" };
for (int i = 0; i < partitions.length; i++) {
+ final PackageManagerService.SystemPartition systemPartition =
+ PackageManagerService.SYSTEM_PARTITIONS.get(i);
for (int j = 0; j < appdir.length; j++) {
String canonical = new File("/" + partitions[i]).getCanonicalPath();
String path = String.format("%s/%s/A.apk", canonical, appdir[j]);
- Assert.assertEquals(j == 1 && i != 3,
- PackageManagerService.locationIsPrivileged(path));
+ Assert.assertEquals(j == 1 && i != 3, systemPartition.containsPrivPath(path));
- Assert.assertEquals(i == 1 || i == 2, PackageManagerService.locationIsVendor(path));
- Assert.assertEquals(i == 3, PackageManagerService.locationIsOem(path));
- Assert.assertEquals(i == 4, PackageManagerService.locationIsProduct(path));
- Assert.assertEquals(i == 5, PackageManagerService.locationIsSystemExt(path));
+ final int scanFlag = systemPartition.scanFlag;
+ Assert.assertEquals(i == 1, scanFlag == PackageManagerService.SCAN_AS_VENDOR);
+ Assert.assertEquals(i == 2, scanFlag == PackageManagerService.SCAN_AS_ODM);
+ Assert.assertEquals(i == 3, scanFlag == PackageManagerService.SCAN_AS_OEM);
+ Assert.assertEquals(i == 4, scanFlag == PackageManagerService.SCAN_AS_PRODUCT);
+ Assert.assertEquals(i == 5, scanFlag == PackageManagerService.SCAN_AS_SYSTEM_EXT);
}
}
}
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/AppDataRollbackHelperTest.java b/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java
index 8cb5197f2601..0b8c2a55b45e 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java
@@ -45,8 +45,6 @@ import org.mockito.Mockito;
import java.io.File;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Set;
@RunWith(JUnit4.class)
public class AppDataRollbackHelperTest {
@@ -250,28 +248,22 @@ public class AppDataRollbackHelperTest {
dataForRestore.info.getPackages().add(pendingRestore);
dataForRestore.info.getPackages().add(wasRecentlyRestored);
- Set<Rollback> changed = helper.commitPendingBackupAndRestoreForUser(37,
- Arrays.asList(dataWithPendingBackup, dataWithRecentRestore, dataForDifferentUser,
- dataForRestore));
InOrder inOrder = Mockito.inOrder(installer);
// Check that pending backup and restore for the same package mutually destroyed each other.
+ assertTrue(helper.commitPendingBackupAndRestoreForUser(37, dataWithRecentRestore));
assertEquals(-1, wasRecentlyRestored.getPendingBackups().indexOf(37));
assertNull(wasRecentlyRestored.getRestoreInfo(37));
// Check that backup was performed.
+ assertTrue(helper.commitPendingBackupAndRestoreForUser(37, dataWithPendingBackup));
inOrder.verify(installer).snapshotAppData(eq("com.foo"), eq(37), eq(101),
eq(Installer.FLAG_STORAGE_CE));
assertEquals(-1, pendingBackup.getPendingBackups().indexOf(37));
assertEquals(53, pendingBackup.getCeSnapshotInodes().get(37));
- // Check that changed returns correct Rollback.
- assertEquals(3, changed.size());
- assertTrue(changed.contains(dataWithPendingBackup));
- assertTrue(changed.contains(dataWithRecentRestore));
- assertTrue(changed.contains(dataForRestore));
-
// Check that restore was performed.
+ assertTrue(helper.commitPendingBackupAndRestoreForUser(37, dataForRestore));
inOrder.verify(installer).restoreAppDataSnapshot(
eq("com.abc"), eq(57) /* appId */, eq("seInfo"), eq(37) /* userId */,
eq(17239) /* rollbackId */, eq(Installer.FLAG_STORAGE_CE));
diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
index d27f1c7e0ce7..b5925a6e750f 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
@@ -18,11 +18,18 @@ package com.android.server.rollback;
import static com.google.common.truth.Truth.assertThat;
+import android.content.pm.VersionedPackage;
+import android.content.rollback.PackageRollbackInfo;
+import android.util.IntArray;
+import android.util.SparseLongArray;
+
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
@RunWith(JUnit4.class)
public class RollbackUnitTest {
@@ -74,4 +81,62 @@ public class RollbackUnitTest {
assertThat(rollback.isCommitted()).isTrue();
}
+ @Test
+ public void getPackageNamesAllAndJustApex() {
+ String pkg1 = "test.testpackage.pkg1";
+ String pkg2 = "test.testpackage.pkg2";
+ String pkg3 = "com.blah.hello.three";
+ String pkg4 = "com.something.4pack";
+
+ Rollback rollback = new Rollback(123, new File("/test/testing"), -1);
+ PackageRollbackInfo pkgInfo1 = pkgInfoFor(pkg1, 12, 10, false);
+ PackageRollbackInfo pkgInfo2 = pkgInfoFor(pkg2, 12, 10, true);
+ PackageRollbackInfo pkgInfo3 = pkgInfoFor(pkg3, 12, 10, false);
+ PackageRollbackInfo pkgInfo4 = pkgInfoFor(pkg4, 12, 10, true);
+
+ rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2, pkgInfo3, pkgInfo4));
+
+ assertThat(rollback.getPackageNames()).containsExactly(pkg1, pkg2, pkg3, pkg4);
+ assertThat(rollback.getApexPackageNames()).containsExactly(pkg2, pkg4);
+ }
+
+ @Test
+ public void includesPackages() {
+ String pkg1 = "test.testpackage.pkg1";
+ String pkg2 = "test.testpackage.pkg2";
+ String pkg3 = "com.blah.hello.three";
+ String pkg4 = "com.something.4pack";
+
+ Rollback rollback = new Rollback(123, new File("/test/testing"), -1);
+ PackageRollbackInfo pkgInfo1 = pkgInfoFor(pkg1, 12, 10, false);
+ PackageRollbackInfo pkgInfo2 = pkgInfoFor(pkg2, 18, 12, true);
+ PackageRollbackInfo pkgInfo3 = pkgInfoFor(pkg3, 157, 156, false);
+ PackageRollbackInfo pkgInfo4 = pkgInfoFor(pkg4, 99, 1, true);
+
+ rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2, pkgInfo3, pkgInfo4));
+
+ assertThat(rollback.includesPackage(pkg2)).isTrue();
+ assertThat(rollback.includesPackage(pkg3)).isTrue();
+ assertThat(rollback.includesPackage("com.something.else")).isFalse();
+
+ assertThat(rollback.includesPackageWithDifferentVersion(pkg1, 12)).isFalse();
+ assertThat(rollback.includesPackageWithDifferentVersion(pkg1, 1)).isTrue();
+
+ assertThat(rollback.includesPackageWithDifferentVersion(pkg2, 18)).isFalse();
+ assertThat(rollback.includesPackageWithDifferentVersion(pkg2, 12)).isTrue();
+
+ assertThat(rollback.includesPackageWithDifferentVersion(pkg3, 157)).isFalse();
+ assertThat(rollback.includesPackageWithDifferentVersion(pkg3, 156)).isTrue();
+ assertThat(rollback.includesPackageWithDifferentVersion(pkg3, 15)).isTrue();
+
+ assertThat(rollback.includesPackageWithDifferentVersion(pkg4, 99)).isFalse();
+ assertThat(rollback.includesPackageWithDifferentVersion(pkg4, 100)).isTrue();
+ }
+
+ private static PackageRollbackInfo pkgInfoFor(
+ String packageName, long fromVersion, long toVersion, boolean isApex) {
+ return new PackageRollbackInfo(new VersionedPackage(packageName, fromVersion),
+ new VersionedPackage(packageName, toVersion),
+ new IntArray(), new ArrayList<>(), isApex, new IntArray(), new SparseLongArray());
+ }
}
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/servicestests/src/com/android/server/stats/ProcfsMemoryUtilTest.java b/services/tests/servicestests/src/com/android/server/stats/ProcfsMemoryUtilTest.java
new file mode 100644
index 000000000000..ae5777403528
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/stats/ProcfsMemoryUtilTest.java
@@ -0,0 +1,124 @@
+/*
+ * 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.ProcfsMemoryUtil.parseMemorySnapshotFromStatus;
+import static com.android.server.stats.ProcfsMemoryUtil.parseVmHWMFromStatus;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.stats.ProcfsMemoryUtil.MemorySnapshot;
+
+import org.junit.Test;
+
+/**
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:ProcfsMemoryUtilTest
+ */
+@SmallTest
+public class ProcfsMemoryUtilTest {
+ private static final String STATUS_CONTENTS = "Name:\tandroid.youtube\n"
+ + "State:\tS (sleeping)\n"
+ + "Tgid:\t12088\n"
+ + "Pid:\t12088\n"
+ + "PPid:\t723\n"
+ + "TracerPid:\t0\n"
+ + "Uid:\t10083\t10083\t10083\t10083\n"
+ + "Gid:\t10083\t10083\t10083\t10083\n"
+ + "Ngid:\t0\n"
+ + "FDSize:\t128\n"
+ + "Groups:\t3003 9997 20083 50083 \n"
+ + "VmPeak:\t 4546844 kB\n"
+ + "VmSize:\t 4542636 kB\n"
+ + "VmLck:\t 0 kB\n"
+ + "VmPin:\t 0 kB\n"
+ + "VmHWM:\t 137668 kB\n" // RSS high-water mark
+ + "VmRSS:\t 126776 kB\n" // RSS
+ + "RssAnon:\t 37860 kB\n"
+ + "RssFile:\t 88764 kB\n"
+ + "RssShmem:\t 152 kB\n"
+ + "VmData:\t 4125112 kB\n"
+ + "VmStk:\t 8192 kB\n"
+ + "VmExe:\t 24 kB\n"
+ + "VmLib:\t 102432 kB\n"
+ + "VmPTE:\t 1300 kB\n"
+ + "VmPMD:\t 36 kB\n"
+ + "VmSwap:\t 22 kB\n" // Swap
+ + "Threads:\t95\n"
+ + "SigQ:\t0/13641\n"
+ + "SigPnd:\t0000000000000000\n"
+ + "ShdPnd:\t0000000000000000\n"
+ + "SigBlk:\t0000000000001204\n"
+ + "SigIgn:\t0000000000000001\n"
+ + "SigCgt:\t00000006400084f8\n"
+ + "CapInh:\t0000000000000000\n"
+ + "CapPrm:\t0000000000000000\n"
+ + "CapEff:\t0000000000000000\n"
+ + "CapBnd:\t0000000000000000\n"
+ + "CapAmb:\t0000000000000000\n"
+ + "Seccomp:\t2\n"
+ + "Cpus_allowed:\tff\n"
+ + "Cpus_allowed_list:\t0-7\n"
+ + "Mems_allowed:\t1\n"
+ + "Mems_allowed_list:\t0\n"
+ + "voluntary_ctxt_switches:\t903\n"
+ + "nonvoluntary_ctxt_switches:\t104\n";
+
+ @Test
+ public void testParseVmHWMFromStatus_parsesCorrectValue() {
+ assertThat(parseVmHWMFromStatus(STATUS_CONTENTS)).isEqualTo(137668);
+ }
+
+ @Test
+ public void testParseVmHWMFromStatus_invalidValue() {
+ assertThat(parseVmHWMFromStatus("test\nVmHWM: x0x0x\ntest")).isEqualTo(0);
+ }
+
+ @Test
+ public void testParseVmHWMFromStatus_emptyContents() {
+ assertThat(parseVmHWMFromStatus("")).isEqualTo(0);
+ }
+
+ @Test
+ public void testParseMemorySnapshotFromStatus_parsesCorrectValue() {
+ MemorySnapshot snapshot = parseMemorySnapshotFromStatus(STATUS_CONTENTS);
+ assertThat(snapshot.rssInKilobytes).isEqualTo(126776);
+ assertThat(snapshot.anonRssInKilobytes).isEqualTo(37860);
+ assertThat(snapshot.swapInKilobytes).isEqualTo(22);
+ assertThat(snapshot.isEmpty()).isFalse();
+ }
+
+ @Test
+ public void testParseMemorySnapshotFromStatus_invalidValue() {
+ MemorySnapshot snapshot =
+ parseMemorySnapshotFromStatus("test\nVmRSS:\tx0x0x\nVmSwap:\t1 kB\ntest");
+ assertThat(snapshot.rssInKilobytes).isEqualTo(0);
+ assertThat(snapshot.anonRssInKilobytes).isEqualTo(0);
+ assertThat(snapshot.swapInKilobytes).isEqualTo(1);
+ assertThat(snapshot.isEmpty()).isFalse();
+ }
+
+ @Test
+ public void testParseMemorySnapshotFromStatus_emptyContents() {
+ MemorySnapshot snapshot = parseMemorySnapshotFromStatus("");
+ assertThat(snapshot.rssInKilobytes).isEqualTo(0);
+ assertThat(snapshot.anonRssInKilobytes).isEqualTo(0);
+ assertThat(snapshot.swapInKilobytes).isEqualTo(0);
+ assertThat(snapshot.isEmpty()).isTrue();
+ }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
index e15af3dbecc4..0b4760d89686 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
@@ -68,6 +68,7 @@ public class NotificationComparatorTest extends UiServiceTestCase {
private final int uid2 = 1111111;
private static final String TEST_CHANNEL_ID = "test_channel_id";
+ private NotificationRecord mRecordMinCallNonInterruptive;
private NotificationRecord mRecordMinCall;
private NotificationRecord mRecordHighCall;
private NotificationRecord mRecordDefaultMedia;
@@ -105,6 +106,18 @@ public class NotificationComparatorTest extends UiServiceTestCase {
smsPkg = Settings.Secure.getString(mContext.getContentResolver(),
Settings.Secure.SMS_DEFAULT_APPLICATION);
+ Notification nonInterruptiveNotif = new Notification.Builder(mContext, TEST_CHANNEL_ID)
+ .setCategory(Notification.CATEGORY_CALL)
+ .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
+ .build();
+ mRecordMinCallNonInterruptive = new NotificationRecord(mContext,
+ new StatusBarNotification(callPkg,
+ callPkg, 1, "mRecordMinCallNonInterruptive", callUid, callUid,
+ nonInterruptiveNotif,
+ new UserHandle(userId), "", 2000), getDefaultChannel());
+ mRecordMinCallNonInterruptive.setSystemImportance(NotificationManager.IMPORTANCE_MIN);
+ mRecordMinCallNonInterruptive.setInterruptive(false);
+
Notification n1 = new Notification.Builder(mContext, TEST_CHANNEL_ID)
.setCategory(Notification.CATEGORY_CALL)
.setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
@@ -113,6 +126,7 @@ public class NotificationComparatorTest extends UiServiceTestCase {
callPkg, 1, "minCall", callUid, callUid, n1,
new UserHandle(userId), "", 2000), getDefaultChannel());
mRecordMinCall.setSystemImportance(NotificationManager.IMPORTANCE_MIN);
+ mRecordMinCall.setInterruptive(true);
Notification n2 = new Notification.Builder(mContext, TEST_CHANNEL_ID)
.setCategory(Notification.CATEGORY_CALL)
@@ -245,6 +259,7 @@ public class NotificationComparatorTest extends UiServiceTestCase {
expected.add(mRecordCheater);
expected.add(mRecordCheaterColorized);
expected.add(mRecordMinCall);
+ expected.add(mRecordMinCallNonInterruptive);
List<NotificationRecord> actual = new ArrayList<>();
actual.addAll(expected);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
index 397d2155beeb..a9fe1a62b558 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
@@ -51,6 +51,8 @@ import android.service.notification.NotificationRankingUpdate;
import android.service.notification.SnoozeCriterion;
import android.test.suitebuilder.annotation.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
import com.android.server.UiServiceTestCase;
import org.junit.After;
@@ -61,8 +63,6 @@ import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.List;
-import androidx.test.runner.AndroidJUnit4;
-
@SmallTest
@RunWith(AndroidJUnit4.class)
public class NotificationListenerServiceTest extends UiServiceTestCase {
@@ -116,6 +116,7 @@ public class NotificationListenerServiceTest extends UiServiceTestCase {
assertActionsEqual(getSmartActions(key, i), ranking.getSmartActions());
assertEquals(getSmartReplies(key, i), ranking.getSmartReplies());
assertEquals(canBubble(i), ranking.canBubble());
+ assertEquals(visuallyInterruptive(i), ranking.visuallyInterruptive());
}
}
@@ -182,7 +183,8 @@ public class NotificationListenerServiceTest extends UiServiceTestCase {
tweak.isNoisy(),
(ArrayList) tweak.getSmartActions(),
(ArrayList) tweak.getSmartReplies(),
- tweak.canBubble()
+ tweak.canBubble(),
+ tweak.visuallyInterruptive()
);
assertNotEquals(nru, nru2);
}
@@ -258,7 +260,8 @@ public class NotificationListenerServiceTest extends UiServiceTestCase {
getNoisy(i),
getSmartActions(key, i),
getSmartReplies(key, i),
- canBubble(i)
+ canBubble(i),
+ visuallyInterruptive(i)
);
rankings[i] = ranking;
}
@@ -363,6 +366,10 @@ public class NotificationListenerServiceTest extends UiServiceTestCase {
return index % 4 == 0;
}
+ private boolean visuallyInterruptive(int index) {
+ return index % 4 == 0;
+ }
+
private void assertActionsEqual(
List<Notification.Action> expecteds, List<Notification.Action> actuals) {
assertEquals(expecteds.size(), actuals.size());
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 8444ab2ebeb8..30c8eb36f34a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -658,6 +658,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
@Override
public void onAnimationStart(RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers,
IRemoteAnimationFinishedCallback finishedCallback) {
}
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..1f672c0c95f3 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
@@ -366,7 +366,7 @@ public class ActivityStarterTests extends ActivityTestsBase {
// Never review permissions
doReturn(false).when(mockPackageManager).isPermissionsReviewRequired(any(), anyInt());
- doNothing().when(mockPackageManager).grantEphemeralAccess(
+ doNothing().when(mockPackageManager).grantImplicitAccess(
anyInt(), any(), anyInt(), anyInt());
final Intent intent = new Intent();
@@ -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..d311dfc60675 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -38,7 +38,6 @@ import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
-import android.graphics.Rect;
import android.os.UserHandle;
import android.service.voice.IVoiceInteractionSession;
import android.testing.DexmakerShareClassLoaderRule;
@@ -140,6 +139,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 +216,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 +262,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
@@ -433,12 +445,7 @@ class ActivityTestsBase {
final ActivityStackSupervisor supervisor = mRootActivityContainer.mStackSupervisor;
if (mWindowingMode == WINDOWING_MODE_PINNED) {
stack = new ActivityStack(mDisplay, stackId, supervisor,
- mWindowingMode, ACTIVITY_TYPE_STANDARD, mOnTop) {
- @Override
- Rect getDefaultPictureInPictureBounds(float aspectRatio) {
- return new Rect(50, 50, 100, 100);
- }
- };
+ mWindowingMode, ACTIVITY_TYPE_STANDARD, mOnTop);
} else {
stack = new ActivityStack(mDisplay, stackId, supervisor,
mWindowingMode, mActivityType, mOnTop);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
index 035568f489be..629a95453054 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
@@ -75,6 +75,7 @@ public class AppChangeTransitionTests extends WindowTestsBase {
class TestRemoteAnimationRunner implements IRemoteAnimationRunner {
@Override
public void onAnimationStart(RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers,
IRemoteAnimationFinishedCallback finishedCallback) {
for (RemoteAnimationTarget target : apps) {
assertNotNull(target.startBounds);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index c162b6a5a289..45e68902e123 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -248,6 +248,7 @@ public class AppTransitionTests extends WindowTestsBase {
boolean mCancelled = false;
@Override
public void onAnimationStart(RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers,
IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException {
}
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/PinnedStackControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java
index efd468f1f77a..e9c226340164 100644
--- a/services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java
@@ -66,8 +66,8 @@ public class PinnedStackControllerTest extends WindowTestsBase {
verify(mIPinnedStackListener).onImeVisibilityChanged(false, 0);
verify(mIPinnedStackListener).onShelfVisibilityChanged(false, 0);
- verify(mIPinnedStackListener).onMovementBoundsChanged(any(), any(), any(), eq(false),
- eq(false), anyInt());
+ verify(mIPinnedStackListener).onMovementBoundsChanged(any(), eq(false),
+ eq(false));
verify(mIPinnedStackListener).onActionsChanged(any());
verify(mIPinnedStackListener).onMinimizedStateChanged(anyBoolean());
@@ -75,8 +75,8 @@ public class PinnedStackControllerTest extends WindowTestsBase {
mWm.setShelfHeight(true, SHELF_HEIGHT);
verify(mIPinnedStackListener).onShelfVisibilityChanged(true, SHELF_HEIGHT);
- verify(mIPinnedStackListener).onMovementBoundsChanged(any(), any(), any(), eq(false),
- eq(true), anyInt());
+ verify(mIPinnedStackListener).onMovementBoundsChanged(any(), eq(false),
+ eq(true));
verify(mIPinnedStackListener, never()).onImeVisibilityChanged(anyBoolean(), anyInt());
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index 9ca0180e507d..f792b0db6cb5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -26,13 +26,17 @@ import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast;
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.reset;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
+import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_TOP;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -44,9 +48,12 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import android.app.ActivityManager.TaskSnapshot;
import android.os.Binder;
+import android.os.IBinder;
import android.os.IInterface;
import android.platform.test.annotations.Presubmit;
import android.util.SparseBooleanArray;
@@ -88,8 +95,8 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
doReturn(mDisplayContent).when(mWm.mRoot).getDisplayContent(anyInt());
}
when(mMockRunner.asBinder()).thenReturn(new Binder());
- mController = new RecentsAnimationController(mWm, mMockRunner, mAnimationCallbacks,
- DEFAULT_DISPLAY);
+ mController = spy(new RecentsAnimationController(mWm, mMockRunner, mAnimationCallbacks,
+ DEFAULT_DISPLAY));
}
@Test
@@ -133,11 +140,11 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
@Test
public void testIncludedApps_expectTargetAndVisible() {
mWm.setRecentsAnimationController(mController);
- final ActivityStack homStack = mDisplayContent.mAcitvityDisplay.getOrCreateStack(
+ final ActivityStack homeStack = mDisplayContent.mAcitvityDisplay.getOrCreateStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
final AppWindowToken homeAppWindow =
new ActivityTestsBase.ActivityBuilder(mWm.mAtmService)
- .setStack(homStack)
+ .setStack(homeStack)
.setCreateTask(true)
.build()
.mAppWindowToken;
@@ -157,6 +164,102 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
}
@Test
+ public void testWallpaperIncluded_expectTarget() throws Exception {
+ mWm.setRecentsAnimationController(mController);
+ final ActivityStack homeStack = mDisplayContent.mAcitvityDisplay.getOrCreateStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
+ final AppWindowToken homeAppWindow =
+ new ActivityTestsBase.ActivityBuilder(mWm.mAtmService)
+ .setStack(homeStack)
+ .setCreateTask(true)
+ .build()
+ .mAppWindowToken;
+ final AppWindowToken appWindow = createAppWindowToken(mDisplayContent,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, appWindow, "win1");
+ appWindow.addWindow(win1);
+ final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm,
+ mock(IBinder.class), true, mDisplayContent, true /* ownerCanManageAppTokens */);
+ spyOn(mDisplayContent.mWallpaperController);
+ doReturn(true).when(mDisplayContent.mWallpaperController).isWallpaperVisible();
+
+ mDisplayContent.getConfiguration().windowConfiguration.setRotation(
+ mDisplayContent.getRotation());
+ mController.initialize(ACTIVITY_TYPE_HOME, new SparseBooleanArray());
+ mController.startAnimation();
+
+ // Ensure that we are animating the app and wallpaper target
+ assertTrue(mController.isAnimatingTask(appWindow.getTask()));
+ assertTrue(mController.isAnimatingWallpaper(wallpaperWindowToken));
+ }
+
+ @Test
+ public void testWallpaperAnimatorCanceled_expectAnimationKeepsRunning() throws Exception {
+ mWm.setRecentsAnimationController(mController);
+ final ActivityStack homeStack = mDisplayContent.mAcitvityDisplay.getOrCreateStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
+ final AppWindowToken homeAppWindow =
+ new ActivityTestsBase.ActivityBuilder(mWm.mAtmService)
+ .setStack(homeStack)
+ .setCreateTask(true)
+ .build()
+ .mAppWindowToken;
+ final AppWindowToken appWindow = createAppWindowToken(mDisplayContent,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, appWindow, "win1");
+ appWindow.addWindow(win1);
+ final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm,
+ mock(IBinder.class), true, mDisplayContent, true /* ownerCanManageAppTokens */);
+ spyOn(mDisplayContent.mWallpaperController);
+ doReturn(true).when(mDisplayContent.mWallpaperController).isWallpaperVisible();
+
+ mDisplayContent.getConfiguration().windowConfiguration.setRotation(
+ mDisplayContent.getRotation());
+ mController.initialize(ACTIVITY_TYPE_HOME, new SparseBooleanArray());
+ mController.startAnimation();
+
+ // Cancel the animation and ensure the controller is still running
+ wallpaperWindowToken.cancelAnimation();
+ assertTrue(mController.isAnimatingTask(appWindow.getTask()));
+ assertFalse(mController.isAnimatingWallpaper(wallpaperWindowToken));
+ verify(mMockRunner, never()).onAnimationCanceled(null /* taskSnapshot */);
+ }
+
+ @Test
+ public void testFinish_expectTargetAndWallpaperAdaptersRemoved() {
+ mWm.setRecentsAnimationController(mController);
+ final ActivityStack homeStack = mDisplayContent.mAcitvityDisplay.getOrCreateStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
+ final AppWindowToken homeAppWindow =
+ new ActivityTestsBase.ActivityBuilder(mWm.mAtmService)
+ .setStack(homeStack)
+ .setCreateTask(true)
+ .build()
+ .mAppWindowToken;
+ final WindowState hwin1 = createWindow(null, TYPE_BASE_APPLICATION, homeAppWindow, "hwin1");
+ homeAppWindow.addWindow(hwin1);
+ final AppWindowToken appWindow = createAppWindowToken(mDisplayContent,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, appWindow, "win1");
+ appWindow.addWindow(win1);
+ final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm,
+ mock(IBinder.class), true, mDisplayContent, true /* ownerCanManageAppTokens */);
+ spyOn(mDisplayContent.mWallpaperController);
+ doReturn(true).when(mDisplayContent.mWallpaperController).isWallpaperVisible();
+
+ // Start and finish the animation
+ mController.initialize(ACTIVITY_TYPE_HOME, new SparseBooleanArray());
+ mController.startAnimation();
+ // Reset at this point since we may remove adapters that couldn't be created
+ reset(mController);
+ mController.cleanupAnimation(REORDER_MOVE_TO_TOP);
+
+ // Ensure that we remove the task (home & app) and wallpaper adapters
+ verify(mController, times(2)).removeAnimation(any());
+ verify(mController, times(1)).removeWallpaperAnimation(any());
+ }
+
+ @Test
public void testDeferCancelAnimation() throws Exception {
mWm.setRecentsAnimationController(mController);
final AppWindowToken appWindow = createAppWindowToken(mDisplayContent,
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index 3e05dcc0f297..3b9c3bbdc4ba 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -19,7 +19,9 @@ package com.android.server.wm;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
@@ -27,10 +29,12 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Binder;
+import android.os.IBinder;
import android.os.IInterface;
import android.platform.test.annotations.Presubmit;
import android.view.IRemoteAnimationFinishedCallback;
@@ -96,9 +100,12 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
- verify(mMockRunner).onAnimationStart(appsCaptor.capture(), finishedCaptor.capture());
+ verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+ finishedCaptor.capture());
assertEquals(1, appsCaptor.getValue().length);
final RemoteAnimationTarget app = appsCaptor.getValue()[0];
assertEquals(new Point(50, 100), app.position);
@@ -201,9 +208,12 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
- verify(mMockRunner).onAnimationStart(appsCaptor.capture(), finishedCaptor.capture());
+ verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+ finishedCaptor.capture());
assertEquals(1, appsCaptor.getValue().length);
assertEquals(mMockLeash, appsCaptor.getValue()[0].leash);
}
@@ -237,9 +247,12 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
- verify(mMockRunner).onAnimationStart(appsCaptor.capture(), finishedCaptor.capture());
+ verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+ finishedCaptor.capture());
assertEquals(1, appsCaptor.getValue().length);
final RemoteAnimationTarget app = appsCaptor.getValue()[0];
assertEquals(RemoteAnimationTarget.MODE_CHANGING, app.mode);
@@ -264,6 +277,66 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
}
}
+ @Test
+ public void testWallpaperIncluded_expectTarget() throws Exception {
+ final WindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm, mock(IBinder.class),
+ true, mDisplayContent, true /* ownerCanManageAppTokens */);
+ spyOn(mDisplayContent.mWallpaperController);
+ doReturn(true).when(mDisplayContent.mWallpaperController).isWallpaperVisible();
+ final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ mDisplayContent.mOpeningApps.add(win.mAppToken);
+ try {
+ final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mAppToken,
+ new Point(50, 100), new Rect(50, 100, 150, 150), null).mAdapter;
+ adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
+ mController.goodToGo();
+ mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
+ ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
+ verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+ finishedCaptor.capture());
+ assertEquals(1, wallpapersCaptor.getValue().length);
+ } finally {
+ mDisplayContent.mOpeningApps.clear();
+ }
+ }
+
+ @Test
+ public void testWallpaperAnimatorCanceled_expectAnimationKeepsRunning() throws Exception {
+ final WindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm, mock(IBinder.class),
+ true, mDisplayContent, true /* ownerCanManageAppTokens */);
+ spyOn(mDisplayContent.mWallpaperController);
+ doReturn(true).when(mDisplayContent.mWallpaperController).isWallpaperVisible();
+ final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ mDisplayContent.mOpeningApps.add(win.mAppToken);
+ try {
+ final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mAppToken,
+ new Point(50, 100), new Rect(50, 100, 150, 150), null).mAdapter;
+ adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
+ mController.goodToGo();
+ mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
+ ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
+ verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+ finishedCaptor.capture());
+ assertEquals(1, wallpapersCaptor.getValue().length);
+
+ // Cancel the wallpaper window animator and ensure the runner is not canceled
+ wallpaperWindowToken.cancelAnimation();
+ verify(mMockRunner, never()).onAnimationCancelled();
+ } finally {
+ mDisplayContent.mOpeningApps.clear();
+ }
+ }
+
private static void verifyNoMoreInteractionsExceptAsBinder(IInterface binder) {
verify(binder, atLeast(0)).asBinder();
verifyNoMoreInteractions(binder);
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/SurfaceAnimatorTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
index 340e7411d21e..2b1c4fff5861 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
@@ -138,6 +138,19 @@ public class SurfaceAnimatorTest extends WindowTestsBase {
}
@Test
+ public void testCancelWithNullFinishCallbackAnimation() {
+ SurfaceAnimator animator = new SurfaceAnimator(mAnimatable, null, mWm);
+ animator.startAnimation(mTransaction, mSpec, true /* hidden */);
+ assertTrue(animator.isAnimating());
+ assertNotNull(animator.getAnimation());
+ animator.cancelAnimation();
+ assertFalse(animator.isAnimating());
+ assertNull(animator.getAnimation());
+ verify(mSpec).onAnimationCancelled(any());
+ verify(mTransaction).remove(eq(mAnimatable.mLeash));
+ }
+
+ @Test
public void testDelayingAnimationStart() {
mAnimatable.mSurfaceAnimator.startDelayingAnimationStart();
mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
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 2fe2c41e0036..5a4d3991bedd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -231,7 +231,7 @@ public class SystemServicesTestRule implements TestRule {
new AMTestInjector(mContext, mHandlerThread), mHandlerThread);
spyOn(mAmService);
doReturn(mock(IPackageManager.class)).when(mAmService).getPackageManager();
- doNothing().when(mAmService).grantEphemeralAccessLocked(
+ doNothing().when(mAmService).grantImplicitAccess(
anyInt(), any(), anyInt(), anyInt());
// ActivityManagerInternal
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/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
index 99337565e128..735b9a1dcf2e 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
@@ -325,9 +325,8 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
false /* Don't notify for synchronous calls */);
// Initialize power save, call active state monitoring logic.
- if (status == STATUS_OK && !mRecognitionRequested) {
+ if (status == STATUS_OK) {
initializeTelephonyAndPowerStateListeners();
- mRecognitionRequested = true;
}
return status;
@@ -481,6 +480,9 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
if (unloadModel && modelData.isModelLoaded()) {
Slog.d(TAG, "Unloading previously loaded stale model.");
+ if (mModule == null) {
+ return STATUS_ERROR;
+ }
status = mModule.unloadSoundModel(modelData.getHandle());
MetricsLogger.count(mContext, "sth_unloading_stale_model", 1);
if (status != SoundTrigger.STATUS_OK) {
@@ -550,6 +552,9 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
}
}
+ if (mModule == null) {
+ return STATUS_ERROR;
+ }
int status = mModule.unloadSoundModel(modelData.getHandle());
if (status != SoundTrigger.STATUS_OK) {
Slog.w(TAG, "unloadGenericSoundModel() call failed with " + status);
@@ -878,6 +883,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
mContext.unregisterReceiver(mPowerSaveModeListener);
mPowerSaveModeListener = null;
}
+ mRecognitionRequested = false;
}
// Clears state for all models (generic and keyphrase).
@@ -924,6 +930,9 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
}
private void initializeTelephonyAndPowerStateListeners() {
+ if (mRecognitionRequested) {
+ return;
+ }
long token = Binder.clearCallingIdentity();
try {
// Get the current call state synchronously for the first recognition.
@@ -941,6 +950,8 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
}
mIsPowerSaveMode = mPowerManager.getPowerSaveState(ServiceType.SOUND)
.batterySaverEnabled;
+
+ mRecognitionRequested = true;
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -987,6 +998,9 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
if (exception != null) {
Slog.e(TAG, "forceStopAndUnloadModel", exception);
}
+ if (mModule == null) {
+ return;
+ }
if (modelData.isModelStarted()) {
Slog.d(TAG, "Stopping previously started dangling model " + modelData.getHandle());
if (mModule.stopRecognition(modelData.getHandle()) != STATUS_OK) {
@@ -1093,6 +1107,13 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
// a recognition include: no active phone call or not being in a power save mode. Also,
// the native service should be enabled.
private boolean isRecognitionAllowed() {
+ // if mRecognitionRequested is false, call and power state listeners are not registered so
+ // we read current state directly from services
+ if (!mRecognitionRequested) {
+ mCallActive = mTelephonyManager.getCallState() == TelephonyManager.CALL_STATE_OFFHOOK;
+ mIsPowerSaveMode =
+ mPowerManager.getPowerSaveState(ServiceType.SOUND).batterySaverEnabled;
+ }
return !mCallActive && !mServiceDisabled && !mIsPowerSaveMode;
}
@@ -1116,6 +1137,9 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
return STATUS_OK;
}
+ if (mModule == null) {
+ return STATUS_ERROR;
+ }
int status = mModule.startRecognition(handle, config);
if (status != SoundTrigger.STATUS_OK) {
Slog.w(TAG, "startRecognition failed with " + status);
@@ -1152,8 +1176,11 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
}
private int stopRecognitionLocked(ModelData modelData, boolean notify) {
- IRecognitionStatusCallback callback = modelData.getCallback();
+ if (mModule == null) {
+ return STATUS_ERROR;
+ }
+ IRecognitionStatusCallback callback = modelData.getCallback();
// Stop recognition.
int status = STATUS_OK;
diff --git a/services/wifi/java/android/net/wifi/WifiStackClient.java b/services/wifi/java/android/net/wifi/WifiStackClient.java
index fa66e4c5eeea..64af7a8845a7 100644
--- a/services/wifi/java/android/net/wifi/WifiStackClient.java
+++ b/services/wifi/java/android/net/wifi/WifiStackClient.java
@@ -21,6 +21,7 @@ import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
import android.annotation.NonNull;
import android.content.Context;
import android.net.ConnectivityModuleConnector;
+import android.os.HandlerThread;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -56,13 +57,20 @@ public class WifiStackClient {
public void onModuleServiceConnected(IBinder service) {
Log.i(TAG, "Wifi stack connected");
- registerWifiStackService(service);
- IWifiStackConnector connector = IWifiStackConnector.Stub.asInterface(service);
- registerApiServiceAndStart(connector, Context.WIFI_SERVICE);
- registerApiServiceAndStart(connector, Context.WIFI_SCANNING_SERVICE);
- registerApiServiceAndStart(connector, Context.WIFI_P2P_SERVICE);
- registerApiServiceAndStart(connector, Context.WIFI_AWARE_SERVICE);
- registerApiServiceAndStart(connector, Context.WIFI_RTT_RANGING_SERVICE);
+ // spin up a new thread to not block system_server main thread
+ HandlerThread thread = new HandlerThread("InitWifiServicesThread");
+ thread.start();
+ thread.getThreadHandler().post(() -> {
+ registerWifiStackService(service);
+ IWifiStackConnector connector = IWifiStackConnector.Stub.asInterface(service);
+ registerApiServiceAndStart(connector, Context.WIFI_SCANNING_SERVICE);
+ registerApiServiceAndStart(connector, Context.WIFI_SERVICE);
+ registerApiServiceAndStart(connector, Context.WIFI_P2P_SERVICE);
+ registerApiServiceAndStart(connector, Context.WIFI_AWARE_SERVICE);
+ registerApiServiceAndStart(connector, Context.WIFI_RTT_RANGING_SERVICE);
+
+ thread.quitSafely();
+ });
}
}
diff --git a/startop/apps/test/Android.bp b/startop/apps/test/Android.bp
index 13b7b5ca01c8..a8063209b56f 100644
--- a/startop/apps/test/Android.bp
+++ b/startop/apps/test/Android.bp
@@ -17,6 +17,7 @@
android_app {
name: "startop_test_app",
srcs: [
+ "src/CPUIntensive.java",
"src/EmptyActivity.java",
"src/LayoutInflationActivity.java",
"src/ComplexLayoutInflationActivity.java",
@@ -24,5 +25,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/apps/test/src/CPUIntensive.java b/startop/apps/test/src/CPUIntensive.java
new file mode 100644
index 000000000000..a411e8ce8ce0
--- /dev/null
+++ b/startop/apps/test/src/CPUIntensive.java
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+/**
+ * A threaded CPU intensive class for use in benchmarks.
+ */
+
+package com.android.startop.test;
+
+final class CPUIntensive {
+ public static final int THREAD_COUNT = 8;
+ public static final int ARRAY_SIZE = 30000;
+ public static int[][] array = new int[THREAD_COUNT][ARRAY_SIZE];
+
+ static class WorkerThread extends Thread {
+ int mThreadNumber;
+ WorkerThread(int number) {
+ mThreadNumber = number;
+ }
+ public void run() {
+ final int arrayLength = array[mThreadNumber].length;
+ for (int i = 0; i < arrayLength; ++i) {
+ array[mThreadNumber][i] = i * i;
+ }
+ for (int i = 0; i < arrayLength; ++i) {
+ for (int j = 0; j < arrayLength; ++j) {
+ int swap = array[mThreadNumber][j];
+ array[mThreadNumber][j] = array[mThreadNumber][(j + i) % arrayLength];
+ array[mThreadNumber][(j + i) % arrayLength] = swap;
+ }
+ }
+ }
+ };
+
+ public static void doSomeWork(int threadCount) {
+ WorkerThread[] threads = new WorkerThread[threadCount];
+ // Create the threads.
+ for (int i = 0; i < threadCount; ++i) {
+ threads[i] = new WorkerThread(i);
+ }
+ // Start the threads.
+ for (int i = 0; i < threadCount; ++i) {
+ threads[i].start();
+ }
+ // Join the threads.
+ for (int i = 0; i < threadCount; ++i) {
+ try {
+ threads[i].join();
+ } catch (Exception ex) {
+ }
+ }
+ }
+}
+
diff --git a/startop/apps/test/src/SystemServerBenchmarkActivity.java b/startop/apps/test/src/SystemServerBenchmarkActivity.java
index 61d43226c41e..c8d9fde0bdaf 100644
--- a/startop/apps/test/src/SystemServerBenchmarkActivity.java
+++ b/startop/apps/test/src/SystemServerBenchmarkActivity.java
@@ -17,22 +17,20 @@
package com.android.startop.test;
import android.app.Activity;
+import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.AsyncTask;
import android.os.Bundle;
-import android.os.Trace;
-import android.view.LayoutInflater;
-import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.GridLayout;
import android.widget.TextView;
-import java.util.Arrays;
class Benchmark {
// Time limit to run benchmarks in seconds
@@ -105,6 +103,22 @@ public class SystemServerBenchmarkActivity extends Activity {
new Benchmark(benchmarkList, "Empty", () -> {
});
+ new Benchmark(benchmarkList, "CPU Intensive (1 thread)", () -> {
+ CPUIntensive.doSomeWork(1);
+ });
+
+ new Benchmark(benchmarkList, "CPU Intensive (2 thread)", () -> {
+ CPUIntensive.doSomeWork(2);
+ });
+
+ new Benchmark(benchmarkList, "CPU Intensive (4 thread)", () -> {
+ CPUIntensive.doSomeWork(4);
+ });
+
+ new Benchmark(benchmarkList, "CPU Intensive (8 thread)", () -> {
+ CPUIntensive.doSomeWork(8);
+ });
+
PackageManager pm = getPackageManager();
new Benchmark(benchmarkList, "getInstalledApplications", () -> {
pm.getInstalledApplications(PackageManager.MATCH_SYSTEM_ONLY);
@@ -139,6 +153,10 @@ public class SystemServerBenchmarkActivity extends Activity {
throw new RuntimeException(e);
}
});
+
+ new Benchmark(benchmarkList, "getPackagesForUid", () -> {
+ pm.getPackagesForUid(app.uid);
+ });
} catch (NameNotFoundException e) {
throw new RuntimeException(e);
}
@@ -152,5 +170,45 @@ public class SystemServerBenchmarkActivity extends Activity {
}
});
+ new Benchmark(benchmarkList, "getLaunchIntentForPackage", () -> {
+ pm.getLaunchIntentForPackage("com.android.startop.test");
+ });
+
+ new Benchmark(benchmarkList, "getPackageUid", () -> {
+ try {
+ pm.getPackageUid("com.android.startop.test", 0);
+ } catch (NameNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ });
+
+ new Benchmark(benchmarkList, "checkPermission", () -> {
+ // Check for the first permission I could find.
+ pm.checkPermission("android.permission.SEND_SMS", "com.android.startop.test");
+ });
+
+ new Benchmark(benchmarkList, "checkSignatures", () -> {
+ // Compare with settings, since settings is on both AOSP and Master builds
+ pm.checkSignatures("com.android.settings", "com.android.startop.test");
+ });
+
+ Intent intent = new Intent(Intent.ACTION_BOOT_COMPLETED);
+ new Benchmark(benchmarkList, "queryBroadcastReceivers", () -> {
+ pm.queryBroadcastReceivers(intent, 0);
+ });
+
+ new Benchmark(benchmarkList, "hasSystemFeature", () -> {
+ pm.hasSystemFeature(PackageManager.FEATURE_CAMERA);
+ });
+
+ new Benchmark(benchmarkList, "resolveService", () -> {
+ pm.resolveService(intent, 0);
+ });
+
+ ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
+ new Benchmark(benchmarkList, "getRunningAppProcesses", () -> {
+ am.getRunningAppProcesses();
+ });
+
}
}
diff --git a/startop/view_compiler/Android.bp b/startop/view_compiler/Android.bp
index 4f6524e0528b..c380d291d573 100644
--- a/startop/view_compiler/Android.bp
+++ b/startop/view_compiler/Android.bp
@@ -25,6 +25,7 @@ cc_defaults {
"slicer",
],
static_libs: [
+ "libcutils",
"libtinyxml2",
"liblog",
"libutils",
diff --git a/telecomm/java/android/telecom/CallScreeningService.java b/telecomm/java/android/telecom/CallScreeningService.java
index 0e0406d4035b..e4f8d118e7df 100644
--- a/telecomm/java/android/telecom/CallScreeningService.java
+++ b/telecomm/java/android/telecom/CallScreeningService.java
@@ -16,7 +16,6 @@
package android.telecom;
-import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SdkConstant;
import android.app.Service;
@@ -33,9 +32,6 @@ import com.android.internal.os.SomeArgs;
import com.android.internal.telecom.ICallScreeningAdapter;
import com.android.internal.telecom.ICallScreeningService;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
/**
* This service can be implemented by the default dialer (see
* {@link TelecomManager#getDefaultDialerPackage()}) or a third party app to allow or disallow
@@ -75,7 +71,7 @@ import java.lang.annotation.RetentionPolicy;
*
* public void requestRole() {
* RoleManager roleManager = (RoleManager) getSystemService(ROLE_SERVICE);
- * Intent intent = roleManager.createRequestRoleIntent("android.app.role.CALL_SCREENING");
+ * Intent intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_CALL_SCREENING);
* startActivityForResult(intent, REQUEST_ID);
* }
*
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 35488100fb58..0abd9fc62b14 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -30,6 +30,7 @@ import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.telecom.Logging.Session;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.SomeArgs;
import com.android.internal.telecom.IConnectionService;
import com.android.internal.telecom.IConnectionServiceAdapter;
@@ -2672,4 +2673,13 @@ public abstract class ConnectionService extends Service {
return ++mId;
}
}
+
+ /**
+ * Returns this handler, ONLY FOR TESTING.
+ * @hide
+ */
+ @VisibleForTesting
+ public Handler getHandler() {
+ return mHandler;
+ }
}
diff --git a/telecomm/java/android/telecom/Logging/SessionManager.java b/telecomm/java/android/telecom/Logging/SessionManager.java
index 949f7b7a89ae..49c3a7205d59 100644
--- a/telecomm/java/android/telecom/Logging/SessionManager.java
+++ b/telecomm/java/android/telecom/Logging/SessionManager.java
@@ -391,6 +391,20 @@ public class SessionManager {
return mCurrentThreadId.get();
}
+ /**
+ * @return A String representation of the active sessions at the time that this method is
+ * called.
+ */
+ @VisibleForTesting
+ public synchronized String printActiveSessions() {
+ StringBuilder message = new StringBuilder();
+ for (ConcurrentHashMap.Entry<Integer, Session> entry : mSessionMapper.entrySet()) {
+ message.append(entry.getValue().printFullSessionTree());
+ message.append("\n");
+ }
+ return message.toString();
+ }
+
@VisibleForTesting
public synchronized void cleanupStaleSessions(long timeoutMs) {
String logMessage = "Stale Sessions Cleaned:\n";
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/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index 58f285851375..f527bc3b6df6 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -578,7 +578,8 @@ public class SubscriptionInfo implements Parcelable {
PackageManager packageManager = context.getPackageManager();
PackageInfo packageInfo;
try {
- packageInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
+ packageInfo = packageManager.getPackageInfo(packageName,
+ PackageManager.GET_SIGNING_CERTIFICATES);
} catch (PackageManager.NameNotFoundException e) {
Log.d("SubscriptionInfo", "canManageSubscription: Unknown package: " + packageName, e);
return false;
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index d3cba2e3e889..51de903ed37e 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -2701,7 +2701,8 @@ public class SubscriptionManager {
PackageManager packageManager = mContext.getPackageManager();
PackageInfo packageInfo;
try {
- packageInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
+ packageInfo = packageManager.getPackageInfo(packageName,
+ PackageManager.GET_SIGNING_CERTIFICATES);
} catch (PackageManager.NameNotFoundException e) {
logd("Unknown package: " + packageName);
return false;
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index f25b012389c4..85b548720cb8 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -67,6 +67,7 @@ import android.telephony.VisualVoicemailService.VisualVoicemailTask;
import android.telephony.data.ApnSetting;
import android.telephony.emergency.EmergencyNumber;
import android.telephony.emergency.EmergencyNumber.EmergencyServiceCategories;
+import android.telephony.ims.ImsMmTelManager;
import android.telephony.ims.aidl.IImsConfig;
import android.telephony.ims.aidl.IImsMmTelFeature;
import android.telephony.ims.aidl.IImsRcsFeature;
@@ -270,9 +271,6 @@ public class TelephonyManager {
private SubscriptionManager mSubscriptionManager;
private TelephonyScanManager mTelephonyScanManager;
- private static String multiSimConfig =
- SystemProperties.get(TelephonyProperties.PROPERTY_MULTI_SIM_CONFIG);
-
/** Enum indicating multisim variants
* DSDS - Dual SIM Dual Standby
* DSDA - Dual SIM Dual Active
@@ -364,7 +362,6 @@ public class TelephonyManager {
}
}
-
/**
* Returns the number of phones available.
* Returns 0 if none of voice, sms, data is not supported
@@ -397,6 +394,31 @@ public class TelephonyManager {
return phoneCount;
}
+ /**
+ *
+ * Return how many phone / logical modem can be active simultaneously, in terms of device
+ * capability.
+ * For example, for a dual-SIM capable device, it always returns 2, even if only one logical
+ * modem / SIM is active (aka in single SIM mode).
+ *
+ * TODO: b/139642279 publicize and rename.
+ * @hide
+ */
+ public static int getMaxPhoneCount() {
+ // TODO: b/139642279 when turning on this feature, remove dependency of
+ // PROPERTY_REBOOT_REQUIRED_ON_MODEM_CHANGE and always return result based on
+ // PROPERTY_MAX_ACTIVE_MODEMS.
+ String rebootRequired = SystemProperties.get(
+ TelephonyProperties.PROPERTY_REBOOT_REQUIRED_ON_MODEM_CHANGE);
+ if (rebootRequired.equals("false")) {
+ // If no reboot is required, return max possible active modems.
+ return SystemProperties.getInt(
+ TelephonyProperties.PROPERTY_MAX_ACTIVE_MODEMS, getDefault().getPhoneCount());
+ } else {
+ return getDefault().getPhoneCount();
+ }
+ }
+
/** {@hide} */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public static TelephonyManager from(Context context) {
@@ -432,8 +454,7 @@ public class TelephonyManager {
/** {@hide} */
@UnsupportedAppUsage
public boolean isMultiSimEnabled() {
- return (multiSimConfig.equals("dsds") || multiSimConfig.equals("dsda") ||
- multiSimConfig.equals("tsts"));
+ return getPhoneCount() > 1;
}
//
@@ -3251,6 +3272,31 @@ public class TelephonyManager {
}
}
+
+ /**
+ * Returns true if the specified type of application (e.g. {@link #APPTYPE_CSIM} is present
+ * on the UICC card.
+ *
+ * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission
+ *
+ * @param appType the uicc app type like {@link APPTYPE_CSIM}
+ * @return true if the specified type of application in UICC CARD or false if no uicc or error.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public boolean isApplicationOnUicc(@UiccAppType int appType) {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ return service.isApplicationOnUicc(getSubId(), appType);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#isApplicationOnUicc", e);
+ }
+ return false;
+ }
+
/**
* Returns a constant indicating the state of the device SIM card in a logical slot.
*
@@ -6600,11 +6646,7 @@ public class TelephonyManager {
public int getSimCount() {
// FIXME Need to get it from Telephony Dev Controller when that gets implemented!
// and then this method shouldn't be used at all!
- if(isMultiSimEnabled()) {
- return getPhoneCount();
- } else {
- return 1;
- }
+ return getPhoneCount();
}
/**
@@ -8630,7 +8672,12 @@ public class TelephonyManager {
return -1;
}
- /** @hide */
+ /**
+ * @deprecated Use {@link android.telephony.ims.ImsMmTelManager#setVtSettingEnabled(boolean)}
+ * instead.
+ * @hide
+ */
+ @Deprecated
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public void enableVideoCalling(boolean enable) {
@@ -8643,7 +8690,14 @@ public class TelephonyManager {
}
}
- /** @hide */
+ /**
+ * @deprecated Use {@link ImsMmTelManager#isVtSettingEnabled()} instead to check if the user
+ * has enabled the Video Calling setting, {@link ImsMmTelManager#isAvailable(int, int)} to
+ * determine if video calling is available, or {@link ImsMmTelManager#isCapable(int, int)} to
+ * determine if video calling is capable.
+ * @hide
+ */
+ @Deprecated
@SystemApi
@RequiresPermission(anyOf = {
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
@@ -8763,6 +8817,7 @@ public class TelephonyManager {
* @param subId Subscription ID
* @return true if IMS status is registered, false if the IMS status is not registered or a
* RemoteException occurred.
+ * Use {@link ImsMmTelManager.RegistrationCallback} instead.
* @hide
*/
public boolean isImsRegistered(int subId) {
@@ -8799,6 +8854,8 @@ public class TelephonyManager {
* used during creation, the default subscription ID will be used.
* @return true if Voice over LTE is available or false if it is unavailable or unknown.
* @see SubscriptionManager#getDefaultSubscriptionId()
+ * <p>
+ * Use {@link ImsMmTelManager#isAvailable(int, int)} instead.
* @hide
*/
@UnsupportedAppUsage
@@ -8818,6 +8875,7 @@ public class TelephonyManager {
* used during creation, the default subscription ID will be used. To query the
* underlying technology that VT is available on, use {@link #getImsRegTechnologyForMmTel}.
* @return true if VT is available, or false if it is unavailable or unknown.
+ * Use {@link ImsMmTelManager#isAvailable(int, int)} instead.
* @hide
*/
@UnsupportedAppUsage
@@ -8833,6 +8891,7 @@ public class TelephonyManager {
* Returns the Status of Wi-Fi calling (Voice over WiFi) for the subscription ID specified.
* @param subId the subscription ID.
* @return true if VoWiFi is available, or false if it is unavailable or unknown.
+ * Use {@link ImsMmTelManager#isAvailable(int, int)} instead.
* @hide
*/
@UnsupportedAppUsage
@@ -8853,6 +8912,7 @@ public class TelephonyManager {
* - {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN} for IWLAN registration, or
* - {@link ImsRegistrationImplBase#REGISTRATION_TECH_NONE} if we are not registered or the
* result is unavailable.
+ * Use {@link ImsMmTelManager.RegistrationCallback} instead.
* @hide
*/
public @ImsRegistrationImplBase.ImsRegistrationTech int getImsRegTechnologyForMmTel() {
diff --git a/telephony/java/android/telephony/UiccAccessRule.java b/telephony/java/android/telephony/UiccAccessRule.java
index 811722f0bbff..93ccba1dd996 100644
--- a/telephony/java/android/telephony/UiccAccessRule.java
+++ b/telephony/java/android/telephony/UiccAccessRule.java
@@ -20,6 +20,7 @@ import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.content.pm.PackageInfo;
import android.content.pm.Signature;
+import android.content.pm.SigningInfo;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -169,17 +170,28 @@ public final class UiccAccessRule implements Parcelable {
*
* @param packageInfo package info fetched from
* {@link android.content.pm.PackageManager#getPackageInfo}.
- * {@link android.content.pm.PackageManager#GET_SIGNATURES} must have been passed in.
+ * {@link android.content.pm.PackageManager#GET_SIGNING_CERTIFICATES} must have been
+ * passed in.
* @return either {@link TelephonyManager#CARRIER_PRIVILEGE_STATUS_HAS_ACCESS} or
* {@link TelephonyManager#CARRIER_PRIVILEGE_STATUS_NO_ACCESS}.
*/
public int getCarrierPrivilegeStatus(PackageInfo packageInfo) {
- if (packageInfo.signatures == null || packageInfo.signatures.length == 0) {
+ Signature[] signatures = packageInfo.signatures;
+ SigningInfo sInfo = packageInfo.signingInfo;
+
+ if (sInfo != null) {
+ signatures = sInfo.getSigningCertificateHistory();
+ if (sInfo.hasMultipleSigners()) {
+ signatures = sInfo.getApkContentsSigners();
+ }
+ }
+
+ if (signatures == null || signatures.length == 0) {
throw new IllegalArgumentException(
- "Must use GET_SIGNATURES when looking up package info");
+ "Must use GET_SIGNING_CERTIFICATES when looking up package info");
}
- for (Signature sig : packageInfo.signatures) {
+ for (Signature sig : signatures) {
int accessStatus = getCarrierPrivilegeStatus(sig, packageInfo.packageName);
if (accessStatus != TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS) {
return accessStatus;
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index 77ee20512d11..4ddeb908a200 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -309,6 +309,37 @@ public final class ImsCallProfile implements Parcelable {
public @CallRestrictCause int mRestrictCause = CALL_RESTRICT_CAUSE_NONE;
/**
+ * The VERSTAT for an incoming call's phone number.
+ */
+ private @VerificationStatus int mCallerNumberVerificationStatus;
+
+ /**
+ * Indicates that the network could not perform verification.
+ */
+ public static final int VERIFICATION_STATUS_NOT_VERIFIED = 0;
+
+ /**
+ * Indicates that verification by the network passed. This indicates there is a high likelihood
+ * that the call originated from a valid source.
+ */
+ public static final int VERIFICATION_STATUS_PASSED = 1;
+
+ /**
+ * Indicates that verification by the network failed. This indicates there is a high likelihood
+ * that the call did not originate from a valid source.
+ */
+ public static final int VERIFICATION_STATUS_FAILED = 2;
+
+ /**@hide*/
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "VERIFICATION_STATUS_", value = {
+ VERIFICATION_STATUS_NOT_VERIFIED,
+ VERIFICATION_STATUS_PASSED,
+ VERIFICATION_STATUS_FAILED
+ })
+ public @interface VerificationStatus {}
+
+ /**
* The emergency service categories, only valid if {@link #getServiceType} returns
* {@link #SERVICE_TYPE_EMERGENCY}
*
@@ -539,6 +570,29 @@ public final class ImsCallProfile implements Parcelable {
mMediaProfile = profile.mMediaProfile;
}
+ /**
+ * Sets the verification status for the phone number of an incoming call as identified in
+ * ATIS-1000082.
+ * <p>
+ * The ImsService should parse the verstat information from the SIP INVITE headers for the call
+ * to determine this information. It is typically found in the P-Asserted-Identity OR From
+ * header fields.
+ * @param callerNumberVerificationStatus the new verification status.
+ */
+ public void setCallerNumberVerificationStatus(
+ @VerificationStatus int callerNumberVerificationStatus) {
+ mCallerNumberVerificationStatus = callerNumberVerificationStatus;
+ }
+
+ /**
+ * Gets the verification status for the phone number of an incoming call as identified in
+ * ATIS-1000082.
+ * @return the verification status.
+ */
+ public @VerificationStatus int getCallerNumberVerificationStatus() {
+ return mCallerNumberVerificationStatus;
+ }
+
@NonNull
@Override
public String toString() {
@@ -551,7 +605,8 @@ public final class ImsCallProfile implements Parcelable {
+ ", emergencyCallRouting=" + mEmergencyCallRouting
+ ", emergencyCallTesting=" + mEmergencyCallTesting
+ ", hasKnownUserIntentEmergency=" + mHasKnownUserIntentEmergency
- + ", mRestrictCause=" + mRestrictCause + " }";
+ + ", mRestrictCause=" + mRestrictCause
+ + ", mCallerNumberVerstat= " + mCallerNumberVerificationStatus + " }";
}
@Override
@@ -572,6 +627,7 @@ public final class ImsCallProfile implements Parcelable {
out.writeBoolean(mEmergencyCallTesting);
out.writeBoolean(mHasKnownUserIntentEmergency);
out.writeInt(mRestrictCause);
+ out.writeInt(mCallerNumberVerificationStatus);
}
private void readFromParcel(Parcel in) {
@@ -585,6 +641,7 @@ public final class ImsCallProfile implements Parcelable {
mEmergencyCallTesting = in.readBoolean();
mHasKnownUserIntentEmergency = in.readBoolean();
mRestrictCause = in.readInt();
+ mCallerNumberVerificationStatus = in.readInt();
}
public static final @android.annotation.NonNull Creator<ImsCallProfile> CREATOR = new Creator<ImsCallProfile>() {
diff --git a/telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl b/telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl
index 606df15b1782..5aa58c1ee7ee 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl
@@ -21,8 +21,7 @@ package android.telephony.ims.aidl;
* {@hide}
*/
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 onSendSmsResult(int token, int messageRef, int status, int reason, int networkErrorCode);
+ 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..175769bd34e4 100644
--- a/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java
@@ -118,6 +118,12 @@ public class ImsSmsImplBase {
*/
public static final int STATUS_REPORT_STATUS_ERROR = 2;
+ /**
+ * No network error was generated while processing the SMS message.
+ */
+ // Should match SmsResponse.NO_ERROR_CODE
+ public static final int RESULT_NO_NETWORK_ERROR = -1;
+
// Lock for feature synchronization
private final Object mLock = new Object();
private IImsSmsListener mListener;
@@ -147,7 +153,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 +172,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 +212,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 {
@@ -229,16 +237,37 @@ public class ImsSmsImplBase {
}
/**
+ * This method should be triggered by the IMS providers when an outgoing SMS message has been
+ * sent successfully.
+ *
+ * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
+ * @param messageRef the message reference. Should be between 0 and 255 per TS.123.040
+ *
+ * @throws RuntimeException if called before {@link #onReady()} is triggered or if the
+ * connection to the framework is not available. If this happens attempting to send the SMS
+ * should be aborted.
+ */
+ public final void onSendSmsResultSuccess(int token, int messageRef) throws RuntimeException {
+ synchronized (mLock) {
+ if (mListener == null) {
+ throw new RuntimeException("Feature not ready.");
+ }
+ try {
+ mListener.onSendSmsResult(token, messageRef, SEND_STATUS_OK,
+ SmsManager.RESULT_ERROR_NONE, RESULT_NO_NETWORK_ERROR);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
* This method should be triggered by the IMS providers to pass the result of the sent message
* to the platform.
*
* @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
* @param messageRef the message reference. Should be between 0 and 255 per TS.123.040
- * @param status result of sending the SMS. Valid values are:
- * {@link #SEND_STATUS_OK},
- * {@link #SEND_STATUS_ERROR},
- * {@link #SEND_STATUS_ERROR_RETRY},
- * {@link #SEND_STATUS_ERROR_FALLBACK},
+ * @param status result of sending the SMS.
* @param reason reason in case status is failure. Valid values are:
* {@link SmsManager#RESULT_ERROR_NONE},
* {@link SmsManager#RESULT_ERROR_GENERIC_FAILURE},
@@ -269,7 +298,11 @@ public class ImsSmsImplBase {
* @throws RuntimeException if called before {@link #onReady()} is triggered or if the
* connection to the framework is not available. If this happens attempting to send the SMS
* should be aborted.
+ * @deprecated Use {@link #onSendSmsResultSuccess(int, int)} or
+ * {@link #onSendSmsResultError(int, int, int, int, int)} to notify the framework of the SMS
+ * send result.
*/
+ @Deprecated
public final void onSendSmsResult(int token, int messageRef, @SendStatusResult int status,
int reason) throws RuntimeException {
synchronized (mLock) {
@@ -277,7 +310,8 @@ public class ImsSmsImplBase {
throw new RuntimeException("Feature not ready.");
}
try {
- mListener.onSendSmsResult(token, messageRef, status, reason);
+ mListener.onSendSmsResult(token, messageRef, status, reason,
+ RESULT_NO_NETWORK_ERROR);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
@@ -285,23 +319,89 @@ public class ImsSmsImplBase {
}
/**
- * Sets the status report of the sent message.
+ * This method should be triggered by the IMS providers when an outgoing message fails to be
+ * sent due to an error generated while processing the message or after being sent to the
+ * network.
*
* @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
+ * @param messageRef the message reference. Should be between 0 and 255 per TS.123.040
+ * @param status result of sending the SMS.
+ * @param reason Valid values are:
+ * {@link SmsManager#RESULT_ERROR_NONE},
+ * {@link SmsManager#RESULT_ERROR_GENERIC_FAILURE},
+ * {@link SmsManager#RESULT_ERROR_RADIO_OFF},
+ * {@link SmsManager#RESULT_ERROR_NULL_PDU},
+ * {@link SmsManager#RESULT_ERROR_NO_SERVICE},
+ * {@link SmsManager#RESULT_ERROR_LIMIT_EXCEEDED},
+ * {@link SmsManager#RESULT_ERROR_FDN_CHECK_FAILURE},
+ * {@link SmsManager#RESULT_ERROR_SHORT_CODE_NOT_ALLOWED},
+ * {@link SmsManager#RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED},
+ * {@link SmsManager#RESULT_RADIO_NOT_AVAILABLE},
+ * {@link SmsManager#RESULT_NETWORK_REJECT},
+ * {@link SmsManager#RESULT_INVALID_ARGUMENTS},
+ * {@link SmsManager#RESULT_INVALID_STATE},
+ * {@link SmsManager#RESULT_NO_MEMORY},
+ * {@link SmsManager#RESULT_INVALID_SMS_FORMAT},
+ * {@link SmsManager#RESULT_SYSTEM_ERROR},
+ * {@link SmsManager#RESULT_MODEM_ERROR},
+ * {@link SmsManager#RESULT_NETWORK_ERROR},
+ * {@link SmsManager#RESULT_ENCODING_ERROR},
+ * {@link SmsManager#RESULT_INVALID_SMSC_ADDRESS},
+ * {@link SmsManager#RESULT_OPERATION_NOT_ALLOWED},
+ * {@link SmsManager#RESULT_INTERNAL_ERROR},
+ * {@link SmsManager#RESULT_NO_RESOURCES},
+ * {@link SmsManager#RESULT_CANCELLED},
+ * {@link SmsManager#RESULT_REQUEST_NOT_SUPPORTED}
+ * @param networkErrorCode the error code reported by the carrier network if sending this SMS
+ * has resulted in an error or {@link #RESULT_NO_NETWORK_ERROR} if no network error was
+ * generated. See 3GPP TS 24.011 Section 7.3.4 for valid error codes and more information.
+ *
+ * @throws RuntimeException if called before {@link #onReady()} is triggered or if the
+ * connection to the framework is not available. If this happens attempting to send the SMS
+ * should be aborted.
+ */
+ public final void onSendSmsResultError(int token, int messageRef, @SendStatusResult int status,
+ int reason, int networkErrorCode)
+ throws RuntimeException {
+ synchronized (mLock) {
+ if (mListener == null) {
+ throw new RuntimeException("Feature not ready.");
+ }
+ try {
+ mListener.onSendSmsResult(token, messageRef, status, reason, networkErrorCode);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * 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 +410,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/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 9f1a2f765eee..866e936a1211 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2012,6 +2012,13 @@ interface ITelephony {
*/
int getRadioHalVersion();
+ /**
+ * Returns true if the specified type of application (e.g. {@link #APPTYPE_CSIM} is present
+ * on the UICC card.
+ * @hide
+ */
+ boolean isApplicationOnUicc(int subId, int appType);
+
boolean isModemEnabledForSlot(int slotIndex, String callingPackage);
boolean isDataEnabledForApn(int apnType, int subId, String callingPackage);
diff --git a/telephony/java/com/android/internal/telephony/TelephonyProperties.java b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
index ef4850716701..4e42c20d28db 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyProperties.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
@@ -234,4 +234,11 @@ public interface TelephonyProperties
String DISPLAY_OPPORTUNISTIC_SUBSCRIPTION_CARRIER_TEXT_PROPERTY_NAME =
"persist.radio.display_opportunistic_carrier";
+ /**
+ * How many logical modems can be active simultaneously. For example, if a device is dual-SIM
+ * capable but currently only one SIM slot and one logical modem is active, this value is still
+ * two.
+ * Type: int
+ */
+ static final String PROPERTY_MAX_ACTIVE_MODEMS = "ro.telephony.max.active.modems";
}
diff --git a/telephony/java/com/google/android/mms/ContentType.java b/telephony/java/com/google/android/mms/ContentType.java
new file mode 100644
index 000000000000..12e4b7e26e1e
--- /dev/null
+++ b/telephony/java/com/google/android/mms/ContentType.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2007-2008 Esmertec AG.
+ * Copyright (C) 2007-2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import java.util.ArrayList;
+
+public class ContentType {
+ public static final String MMS_MESSAGE = "application/vnd.wap.mms-message";
+ // The phony content type for generic PDUs (e.g. ReadOrig.ind,
+ // Notification.ind, Delivery.ind).
+ public static final String MMS_GENERIC = "application/vnd.wap.mms-generic";
+ public static final String MULTIPART_MIXED = "application/vnd.wap.multipart.mixed";
+ public static final String MULTIPART_RELATED = "application/vnd.wap.multipart.related";
+ public static final String MULTIPART_ALTERNATIVE = "application/vnd.wap.multipart.alternative";
+
+ public static final String TEXT_PLAIN = "text/plain";
+ public static final String TEXT_HTML = "text/html";
+ public static final String TEXT_VCALENDAR = "text/x-vCalendar";
+ public static final String TEXT_VCARD = "text/x-vCard";
+
+ public static final String IMAGE_UNSPECIFIED = "image/*";
+ public static final String IMAGE_JPEG = "image/jpeg";
+ public static final String IMAGE_JPG = "image/jpg";
+ public static final String IMAGE_GIF = "image/gif";
+ public static final String IMAGE_WBMP = "image/vnd.wap.wbmp";
+ public static final String IMAGE_PNG = "image/png";
+ public static final String IMAGE_X_MS_BMP = "image/x-ms-bmp";
+
+ public static final String AUDIO_UNSPECIFIED = "audio/*";
+ public static final String AUDIO_AAC = "audio/aac";
+ public static final String AUDIO_AMR = "audio/amr";
+ public static final String AUDIO_IMELODY = "audio/imelody";
+ public static final String AUDIO_MID = "audio/mid";
+ public static final String AUDIO_MIDI = "audio/midi";
+ public static final String AUDIO_MP3 = "audio/mp3";
+ public static final String AUDIO_MPEG3 = "audio/mpeg3";
+ public static final String AUDIO_MPEG = "audio/mpeg";
+ public static final String AUDIO_MPG = "audio/mpg";
+ public static final String AUDIO_MP4 = "audio/mp4";
+ public static final String AUDIO_X_MID = "audio/x-mid";
+ public static final String AUDIO_X_MIDI = "audio/x-midi";
+ public static final String AUDIO_X_MP3 = "audio/x-mp3";
+ public static final String AUDIO_X_MPEG3 = "audio/x-mpeg3";
+ public static final String AUDIO_X_MPEG = "audio/x-mpeg";
+ public static final String AUDIO_X_MPG = "audio/x-mpg";
+ public static final String AUDIO_3GPP = "audio/3gpp";
+ public static final String AUDIO_X_WAV = "audio/x-wav";
+ public static final String AUDIO_OGG = "application/ogg";
+ public static final String AUDIO_OGG2 = "audio/ogg";
+
+ public static final String VIDEO_UNSPECIFIED = "video/*";
+ public static final String VIDEO_3GPP = "video/3gpp";
+ public static final String VIDEO_3G2 = "video/3gpp2";
+ public static final String VIDEO_H263 = "video/h263";
+ public static final String VIDEO_MP4 = "video/mp4";
+
+ public static final String APP_SMIL = "application/smil";
+ public static final String APP_WAP_XHTML = "application/vnd.wap.xhtml+xml";
+ public static final String APP_XHTML = "application/xhtml+xml";
+
+ public static final String APP_DRM_CONTENT = "application/vnd.oma.drm.content";
+ public static final String APP_DRM_MESSAGE = "application/vnd.oma.drm.message";
+
+ private static final ArrayList<String> sSupportedContentTypes = new ArrayList<String>();
+ private static final ArrayList<String> sSupportedImageTypes = new ArrayList<String>();
+ private static final ArrayList<String> sSupportedAudioTypes = new ArrayList<String>();
+ private static final ArrayList<String> sSupportedVideoTypes = new ArrayList<String>();
+
+ static {
+ sSupportedContentTypes.add(TEXT_PLAIN);
+ sSupportedContentTypes.add(TEXT_HTML);
+ sSupportedContentTypes.add(TEXT_VCALENDAR);
+ sSupportedContentTypes.add(TEXT_VCARD);
+
+ sSupportedContentTypes.add(IMAGE_JPEG);
+ sSupportedContentTypes.add(IMAGE_GIF);
+ sSupportedContentTypes.add(IMAGE_WBMP);
+ sSupportedContentTypes.add(IMAGE_PNG);
+ sSupportedContentTypes.add(IMAGE_JPG);
+ sSupportedContentTypes.add(IMAGE_X_MS_BMP);
+ //supportedContentTypes.add(IMAGE_SVG); not yet supported.
+
+ sSupportedContentTypes.add(AUDIO_AAC);
+ sSupportedContentTypes.add(AUDIO_AMR);
+ sSupportedContentTypes.add(AUDIO_IMELODY);
+ sSupportedContentTypes.add(AUDIO_MID);
+ sSupportedContentTypes.add(AUDIO_MIDI);
+ sSupportedContentTypes.add(AUDIO_MP3);
+ sSupportedContentTypes.add(AUDIO_MP4);
+ sSupportedContentTypes.add(AUDIO_MPEG3);
+ sSupportedContentTypes.add(AUDIO_MPEG);
+ sSupportedContentTypes.add(AUDIO_MPG);
+ sSupportedContentTypes.add(AUDIO_X_MID);
+ sSupportedContentTypes.add(AUDIO_X_MIDI);
+ sSupportedContentTypes.add(AUDIO_X_MP3);
+ sSupportedContentTypes.add(AUDIO_X_MPEG3);
+ sSupportedContentTypes.add(AUDIO_X_MPEG);
+ sSupportedContentTypes.add(AUDIO_X_MPG);
+ sSupportedContentTypes.add(AUDIO_X_WAV);
+ sSupportedContentTypes.add(AUDIO_3GPP);
+ sSupportedContentTypes.add(AUDIO_OGG);
+ sSupportedContentTypes.add(AUDIO_OGG2);
+
+ sSupportedContentTypes.add(VIDEO_3GPP);
+ sSupportedContentTypes.add(VIDEO_3G2);
+ sSupportedContentTypes.add(VIDEO_H263);
+ sSupportedContentTypes.add(VIDEO_MP4);
+
+ sSupportedContentTypes.add(APP_SMIL);
+ sSupportedContentTypes.add(APP_WAP_XHTML);
+ sSupportedContentTypes.add(APP_XHTML);
+
+ sSupportedContentTypes.add(APP_DRM_CONTENT);
+ sSupportedContentTypes.add(APP_DRM_MESSAGE);
+
+ // add supported image types
+ sSupportedImageTypes.add(IMAGE_JPEG);
+ sSupportedImageTypes.add(IMAGE_GIF);
+ sSupportedImageTypes.add(IMAGE_WBMP);
+ sSupportedImageTypes.add(IMAGE_PNG);
+ sSupportedImageTypes.add(IMAGE_JPG);
+ sSupportedImageTypes.add(IMAGE_X_MS_BMP);
+
+ // add supported audio types
+ sSupportedAudioTypes.add(AUDIO_AAC);
+ sSupportedAudioTypes.add(AUDIO_AMR);
+ sSupportedAudioTypes.add(AUDIO_IMELODY);
+ sSupportedAudioTypes.add(AUDIO_MID);
+ sSupportedAudioTypes.add(AUDIO_MIDI);
+ sSupportedAudioTypes.add(AUDIO_MP3);
+ sSupportedAudioTypes.add(AUDIO_MPEG3);
+ sSupportedAudioTypes.add(AUDIO_MPEG);
+ sSupportedAudioTypes.add(AUDIO_MPG);
+ sSupportedAudioTypes.add(AUDIO_MP4);
+ sSupportedAudioTypes.add(AUDIO_X_MID);
+ sSupportedAudioTypes.add(AUDIO_X_MIDI);
+ sSupportedAudioTypes.add(AUDIO_X_MP3);
+ sSupportedAudioTypes.add(AUDIO_X_MPEG3);
+ sSupportedAudioTypes.add(AUDIO_X_MPEG);
+ sSupportedAudioTypes.add(AUDIO_X_MPG);
+ sSupportedAudioTypes.add(AUDIO_X_WAV);
+ sSupportedAudioTypes.add(AUDIO_3GPP);
+ sSupportedAudioTypes.add(AUDIO_OGG);
+ sSupportedAudioTypes.add(AUDIO_OGG2);
+
+ // add supported video types
+ sSupportedVideoTypes.add(VIDEO_3GPP);
+ sSupportedVideoTypes.add(VIDEO_3G2);
+ sSupportedVideoTypes.add(VIDEO_H263);
+ sSupportedVideoTypes.add(VIDEO_MP4);
+ }
+
+ // This class should never be instantiated.
+ private ContentType() {
+ }
+
+ @UnsupportedAppUsage
+ public static boolean isSupportedType(String contentType) {
+ return (null != contentType) && sSupportedContentTypes.contains(contentType);
+ }
+
+ @UnsupportedAppUsage
+ public static boolean isSupportedImageType(String contentType) {
+ return isImageType(contentType) && isSupportedType(contentType);
+ }
+
+ @UnsupportedAppUsage
+ public static boolean isSupportedAudioType(String contentType) {
+ return isAudioType(contentType) && isSupportedType(contentType);
+ }
+
+ @UnsupportedAppUsage
+ public static boolean isSupportedVideoType(String contentType) {
+ return isVideoType(contentType) && isSupportedType(contentType);
+ }
+
+ @UnsupportedAppUsage
+ public static boolean isTextType(String contentType) {
+ return (null != contentType) && contentType.startsWith("text/");
+ }
+
+ @UnsupportedAppUsage
+ public static boolean isImageType(String contentType) {
+ return (null != contentType) && contentType.startsWith("image/");
+ }
+
+ @UnsupportedAppUsage
+ public static boolean isAudioType(String contentType) {
+ return (null != contentType) && contentType.startsWith("audio/");
+ }
+
+ @UnsupportedAppUsage
+ public static boolean isVideoType(String contentType) {
+ return (null != contentType) && contentType.startsWith("video/");
+ }
+
+ @UnsupportedAppUsage
+ public static boolean isDrmType(String contentType) {
+ return (null != contentType)
+ && (contentType.equals(APP_DRM_CONTENT)
+ || contentType.equals(APP_DRM_MESSAGE));
+ }
+
+ public static boolean isUnspecified(String contentType) {
+ return (null != contentType) && contentType.endsWith("*");
+ }
+
+ @UnsupportedAppUsage
+ @SuppressWarnings("unchecked")
+ public static ArrayList<String> getImageTypes() {
+ return (ArrayList<String>) sSupportedImageTypes.clone();
+ }
+
+ @UnsupportedAppUsage
+ @SuppressWarnings("unchecked")
+ public static ArrayList<String> getAudioTypes() {
+ return (ArrayList<String>) sSupportedAudioTypes.clone();
+ }
+
+ @UnsupportedAppUsage
+ @SuppressWarnings("unchecked")
+ public static ArrayList<String> getVideoTypes() {
+ return (ArrayList<String>) sSupportedVideoTypes.clone();
+ }
+
+ @SuppressWarnings("unchecked")
+ public static ArrayList<String> getSupportedTypes() {
+ return (ArrayList<String>) sSupportedContentTypes.clone();
+ }
+}
diff --git a/telephony/java/com/google/android/mms/InvalidHeaderValueException.java b/telephony/java/com/google/android/mms/InvalidHeaderValueException.java
new file mode 100644
index 000000000000..2836c3075b3b
--- /dev/null
+++ b/telephony/java/com/google/android/mms/InvalidHeaderValueException.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+/**
+ * Thrown when an invalid header value was set.
+ */
+public class InvalidHeaderValueException extends MmsException {
+ private static final long serialVersionUID = -2053384496042052262L;
+
+ /**
+ * Constructs an InvalidHeaderValueException with no detailed message.
+ */
+ public InvalidHeaderValueException() {
+ super();
+ }
+
+ /**
+ * Constructs an InvalidHeaderValueException with the specified detailed message.
+ *
+ * @param message the detailed message.
+ */
+ @UnsupportedAppUsage
+ public InvalidHeaderValueException(String message) {
+ super(message);
+ }
+}
diff --git a/telephony/java/com/google/android/mms/MmsException.java b/telephony/java/com/google/android/mms/MmsException.java
new file mode 100644
index 000000000000..5be33ed1fac9
--- /dev/null
+++ b/telephony/java/com/google/android/mms/MmsException.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+/**
+ * A generic exception that is thrown by the Mms client.
+ */
+public class MmsException extends Exception {
+ private static final long serialVersionUID = -7323249827281485390L;
+
+ /**
+ * Creates a new MmsException.
+ */
+ @UnsupportedAppUsage
+ public MmsException() {
+ super();
+ }
+
+ /**
+ * Creates a new MmsException with the specified detail message.
+ *
+ * @param message the detail message.
+ */
+ @UnsupportedAppUsage
+ public MmsException(String message) {
+ super(message);
+ }
+
+ /**
+ * Creates a new MmsException with the specified cause.
+ *
+ * @param cause the cause.
+ */
+ @UnsupportedAppUsage
+ public MmsException(Throwable cause) {
+ super(cause);
+ }
+
+ /**
+ * Creates a new MmsException with the specified detail message and cause.
+ *
+ * @param message the detail message.
+ * @param cause the cause.
+ */
+ @UnsupportedAppUsage
+ public MmsException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/telephony/java/com/google/android/mms/package.html b/telephony/java/com/google/android/mms/package.html
new file mode 100755
index 000000000000..c9f96a66ab3b
--- /dev/null
+++ b/telephony/java/com/google/android/mms/package.html
@@ -0,0 +1,5 @@
+<body>
+
+{@hide}
+
+</body>
diff --git a/telephony/java/com/google/android/mms/pdu/AcknowledgeInd.java b/telephony/java/com/google/android/mms/pdu/AcknowledgeInd.java
new file mode 100644
index 000000000000..ae447d7a7417
--- /dev/null
+++ b/telephony/java/com/google/android/mms/pdu/AcknowledgeInd.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import com.google.android.mms.InvalidHeaderValueException;
+
+/**
+ * M-Acknowledge.ind PDU.
+ */
+public class AcknowledgeInd extends GenericPdu {
+ /**
+ * Constructor, used when composing a M-Acknowledge.ind pdu.
+ *
+ * @param mmsVersion current viersion of mms
+ * @param transactionId the transaction-id value
+ * @throws InvalidHeaderValueException if parameters are invalid.
+ * NullPointerException if transactionId is null.
+ */
+ @UnsupportedAppUsage
+ public AcknowledgeInd(int mmsVersion, byte[] transactionId)
+ throws InvalidHeaderValueException {
+ super();
+
+ setMessageType(PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND);
+ setMmsVersion(mmsVersion);
+ setTransactionId(transactionId);
+ }
+
+ /**
+ * Constructor with given headers.
+ *
+ * @param headers Headers for this PDU.
+ */
+ @UnsupportedAppUsage
+ AcknowledgeInd(PduHeaders headers) {
+ super(headers);
+ }
+
+ /**
+ * Get X-Mms-Report-Allowed field value.
+ *
+ * @return the X-Mms-Report-Allowed value
+ */
+ public int getReportAllowed() {
+ return mPduHeaders.getOctet(PduHeaders.REPORT_ALLOWED);
+ }
+
+ /**
+ * Set X-Mms-Report-Allowed field value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ */
+ @UnsupportedAppUsage
+ public void setReportAllowed(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.REPORT_ALLOWED);
+ }
+
+ /**
+ * Get X-Mms-Transaction-Id field value.
+ *
+ * @return the X-Mms-Report-Allowed value
+ */
+ public byte[] getTransactionId() {
+ return mPduHeaders.getTextString(PduHeaders.TRANSACTION_ID);
+ }
+
+ /**
+ * Set X-Mms-Transaction-Id field value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setTransactionId(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.TRANSACTION_ID);
+ }
+}
diff --git a/telephony/java/com/google/android/mms/pdu/Base64.java b/telephony/java/com/google/android/mms/pdu/Base64.java
new file mode 100644
index 000000000000..483fa7f9842e
--- /dev/null
+++ b/telephony/java/com/google/android/mms/pdu/Base64.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+public class Base64 {
+ /**
+ * Used to get the number of Quadruples.
+ */
+ static final int FOURBYTE = 4;
+
+ /**
+ * Byte used to pad output.
+ */
+ static final byte PAD = (byte) '=';
+
+ /**
+ * The base length.
+ */
+ static final int BASELENGTH = 255;
+
+ // Create arrays to hold the base64 characters
+ private static byte[] base64Alphabet = new byte[BASELENGTH];
+
+ // Populating the character arrays
+ static {
+ for (int i = 0; i < BASELENGTH; i++) {
+ base64Alphabet[i] = (byte) -1;
+ }
+ for (int i = 'Z'; i >= 'A'; i--) {
+ base64Alphabet[i] = (byte) (i - 'A');
+ }
+ for (int i = 'z'; i >= 'a'; i--) {
+ base64Alphabet[i] = (byte) (i - 'a' + 26);
+ }
+ for (int i = '9'; i >= '0'; i--) {
+ base64Alphabet[i] = (byte) (i - '0' + 52);
+ }
+
+ base64Alphabet['+'] = 62;
+ base64Alphabet['/'] = 63;
+ }
+
+ /**
+ * Decodes Base64 data into octects
+ *
+ * @param base64Data Byte array containing Base64 data
+ * @return Array containing decoded data.
+ */
+ @UnsupportedAppUsage
+ public static byte[] decodeBase64(byte[] base64Data) {
+ // RFC 2045 requires that we discard ALL non-Base64 characters
+ base64Data = discardNonBase64(base64Data);
+
+ // handle the edge case, so we don't have to worry about it later
+ if (base64Data.length == 0) {
+ return new byte[0];
+ }
+
+ int numberQuadruple = base64Data.length / FOURBYTE;
+ byte decodedData[] = null;
+ byte b1 = 0, b2 = 0, b3 = 0, b4 = 0, marker0 = 0, marker1 = 0;
+
+ // Throw away anything not in base64Data
+
+ int encodedIndex = 0;
+ int dataIndex = 0;
+ {
+ // this sizes the output array properly - rlw
+ int lastData = base64Data.length;
+ // ignore the '=' padding
+ while (base64Data[lastData - 1] == PAD) {
+ if (--lastData == 0) {
+ return new byte[0];
+ }
+ }
+ decodedData = new byte[lastData - numberQuadruple];
+ }
+
+ for (int i = 0; i < numberQuadruple; i++) {
+ dataIndex = i * 4;
+ marker0 = base64Data[dataIndex + 2];
+ marker1 = base64Data[dataIndex + 3];
+
+ b1 = base64Alphabet[base64Data[dataIndex]];
+ b2 = base64Alphabet[base64Data[dataIndex + 1]];
+
+ if (marker0 != PAD && marker1 != PAD) {
+ //No PAD e.g 3cQl
+ b3 = base64Alphabet[marker0];
+ b4 = base64Alphabet[marker1];
+
+ decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
+ decodedData[encodedIndex + 1] =
+ (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
+ decodedData[encodedIndex + 2] = (byte) (b3 << 6 | b4);
+ } else if (marker0 == PAD) {
+ //Two PAD e.g. 3c[Pad][Pad]
+ decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
+ } else if (marker1 == PAD) {
+ //One PAD e.g. 3cQ[Pad]
+ b3 = base64Alphabet[marker0];
+
+ decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
+ decodedData[encodedIndex + 1] =
+ (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
+ }
+ encodedIndex += 3;
+ }
+ return decodedData;
+ }
+
+ /**
+ * Check octect whether it is a base64 encoding.
+ *
+ * @param octect to be checked byte
+ * @return ture if it is base64 encoding, false otherwise.
+ */
+ private static boolean isBase64(byte octect) {
+ if (octect == PAD) {
+ return true;
+ } else if (base64Alphabet[octect] == -1) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * Discards any characters outside of the base64 alphabet, per
+ * the requirements on page 25 of RFC 2045 - "Any characters
+ * outside of the base64 alphabet are to be ignored in base64
+ * encoded data."
+ *
+ * @param data The base-64 encoded data to groom
+ * @return The data, less non-base64 characters (see RFC 2045).
+ */
+ static byte[] discardNonBase64(byte[] data) {
+ byte groomedData[] = new byte[data.length];
+ int bytesCopied = 0;
+
+ for (int i = 0; i < data.length; i++) {
+ if (isBase64(data[i])) {
+ groomedData[bytesCopied++] = data[i];
+ }
+ }
+
+ byte packedData[] = new byte[bytesCopied];
+
+ System.arraycopy(groomedData, 0, packedData, 0, bytesCopied);
+
+ return packedData;
+ }
+}
diff --git a/telephony/java/com/google/android/mms/pdu/CharacterSets.java b/telephony/java/com/google/android/mms/pdu/CharacterSets.java
new file mode 100644
index 000000000000..27da35e2d928
--- /dev/null
+++ b/telephony/java/com/google/android/mms/pdu/CharacterSets.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import java.io.UnsupportedEncodingException;
+import java.util.HashMap;
+
+public class CharacterSets {
+ /**
+ * IANA assigned MIB enum numbers.
+ *
+ * From wap-230-wsp-20010705-a.pdf
+ * Any-charset = <Octet 128>
+ * Equivalent to the special RFC2616 charset value "*"
+ */
+ public static final int ANY_CHARSET = 0x00;
+ public static final int US_ASCII = 0x03;
+ public static final int ISO_8859_1 = 0x04;
+ public static final int ISO_8859_2 = 0x05;
+ public static final int ISO_8859_3 = 0x06;
+ public static final int ISO_8859_4 = 0x07;
+ public static final int ISO_8859_5 = 0x08;
+ public static final int ISO_8859_6 = 0x09;
+ public static final int ISO_8859_7 = 0x0A;
+ public static final int ISO_8859_8 = 0x0B;
+ public static final int ISO_8859_9 = 0x0C;
+ public static final int SHIFT_JIS = 0x11;
+ public static final int UTF_8 = 0x6A;
+ public static final int BIG5 = 0x07EA;
+ public static final int UCS2 = 0x03E8;
+ public static final int UTF_16 = 0x03F7;
+
+ /**
+ * If the encoding of given data is unsupported, use UTF_8 to decode it.
+ */
+ public static final int DEFAULT_CHARSET = UTF_8;
+
+ /**
+ * Array of MIB enum numbers.
+ */
+ private static final int[] MIBENUM_NUMBERS = {
+ ANY_CHARSET,
+ US_ASCII,
+ ISO_8859_1,
+ ISO_8859_2,
+ ISO_8859_3,
+ ISO_8859_4,
+ ISO_8859_5,
+ ISO_8859_6,
+ ISO_8859_7,
+ ISO_8859_8,
+ ISO_8859_9,
+ SHIFT_JIS,
+ UTF_8,
+ BIG5,
+ UCS2,
+ UTF_16,
+ };
+
+ /**
+ * The Well-known-charset Mime name.
+ */
+ public static final String MIMENAME_ANY_CHARSET = "*";
+ public static final String MIMENAME_US_ASCII = "us-ascii";
+ public static final String MIMENAME_ISO_8859_1 = "iso-8859-1";
+ public static final String MIMENAME_ISO_8859_2 = "iso-8859-2";
+ public static final String MIMENAME_ISO_8859_3 = "iso-8859-3";
+ public static final String MIMENAME_ISO_8859_4 = "iso-8859-4";
+ public static final String MIMENAME_ISO_8859_5 = "iso-8859-5";
+ public static final String MIMENAME_ISO_8859_6 = "iso-8859-6";
+ public static final String MIMENAME_ISO_8859_7 = "iso-8859-7";
+ public static final String MIMENAME_ISO_8859_8 = "iso-8859-8";
+ public static final String MIMENAME_ISO_8859_9 = "iso-8859-9";
+ public static final String MIMENAME_SHIFT_JIS = "shift_JIS";
+ public static final String MIMENAME_UTF_8 = "utf-8";
+ public static final String MIMENAME_BIG5 = "big5";
+ public static final String MIMENAME_UCS2 = "iso-10646-ucs-2";
+ public static final String MIMENAME_UTF_16 = "utf-16";
+
+ public static final String DEFAULT_CHARSET_NAME = MIMENAME_UTF_8;
+
+ /**
+ * Array of the names of character sets.
+ */
+ private static final String[] MIME_NAMES = {
+ MIMENAME_ANY_CHARSET,
+ MIMENAME_US_ASCII,
+ MIMENAME_ISO_8859_1,
+ MIMENAME_ISO_8859_2,
+ MIMENAME_ISO_8859_3,
+ MIMENAME_ISO_8859_4,
+ MIMENAME_ISO_8859_5,
+ MIMENAME_ISO_8859_6,
+ MIMENAME_ISO_8859_7,
+ MIMENAME_ISO_8859_8,
+ MIMENAME_ISO_8859_9,
+ MIMENAME_SHIFT_JIS,
+ MIMENAME_UTF_8,
+ MIMENAME_BIG5,
+ MIMENAME_UCS2,
+ MIMENAME_UTF_16,
+ };
+
+ private static final HashMap<Integer, String> MIBENUM_TO_NAME_MAP;
+ private static final HashMap<String, Integer> NAME_TO_MIBENUM_MAP;
+
+ static {
+ // Create the HashMaps.
+ MIBENUM_TO_NAME_MAP = new HashMap<Integer, String>();
+ NAME_TO_MIBENUM_MAP = new HashMap<String, Integer>();
+ assert(MIBENUM_NUMBERS.length == MIME_NAMES.length);
+ int count = MIBENUM_NUMBERS.length - 1;
+ for(int i = 0; i <= count; i++) {
+ MIBENUM_TO_NAME_MAP.put(MIBENUM_NUMBERS[i], MIME_NAMES[i]);
+ NAME_TO_MIBENUM_MAP.put(MIME_NAMES[i], MIBENUM_NUMBERS[i]);
+ }
+ }
+
+ private CharacterSets() {} // Non-instantiatable
+
+ /**
+ * Map an MIBEnum number to the name of the charset which this number
+ * is assigned to by IANA.
+ *
+ * @param mibEnumValue An IANA assigned MIBEnum number.
+ * @return The name string of the charset.
+ * @throws UnsupportedEncodingException
+ */
+ @UnsupportedAppUsage
+ public static String getMimeName(int mibEnumValue)
+ throws UnsupportedEncodingException {
+ String name = MIBENUM_TO_NAME_MAP.get(mibEnumValue);
+ if (name == null) {
+ throw new UnsupportedEncodingException();
+ }
+ return name;
+ }
+
+ /**
+ * Map a well-known charset name to its assigned MIBEnum number.
+ *
+ * @param mimeName The charset name.
+ * @return The MIBEnum number assigned by IANA for this charset.
+ * @throws UnsupportedEncodingException
+ */
+ @UnsupportedAppUsage
+ public static int getMibEnumValue(String mimeName)
+ throws UnsupportedEncodingException {
+ if(null == mimeName) {
+ return -1;
+ }
+
+ Integer mibEnumValue = NAME_TO_MIBENUM_MAP.get(mimeName);
+ if (mibEnumValue == null) {
+ throw new UnsupportedEncodingException();
+ }
+ return mibEnumValue;
+ }
+}
diff --git a/telephony/java/com/google/android/mms/pdu/DeliveryInd.java b/telephony/java/com/google/android/mms/pdu/DeliveryInd.java
new file mode 100644
index 000000000000..7093ac63338c
--- /dev/null
+++ b/telephony/java/com/google/android/mms/pdu/DeliveryInd.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import com.google.android.mms.InvalidHeaderValueException;
+
+/**
+ * M-Delivery.Ind Pdu.
+ */
+public class DeliveryInd extends GenericPdu {
+ /**
+ * Empty constructor.
+ * Since the Pdu corresponding to this class is constructed
+ * by the Proxy-Relay server, this class is only instantiated
+ * by the Pdu Parser.
+ *
+ * @throws InvalidHeaderValueException if error occurs.
+ */
+ public DeliveryInd() throws InvalidHeaderValueException {
+ super();
+ setMessageType(PduHeaders.MESSAGE_TYPE_DELIVERY_IND);
+ }
+
+ /**
+ * Constructor with given headers.
+ *
+ * @param headers Headers for this PDU.
+ */
+ @UnsupportedAppUsage
+ DeliveryInd(PduHeaders headers) {
+ super(headers);
+ }
+
+ /**
+ * Get Date value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public long getDate() {
+ return mPduHeaders.getLongInteger(PduHeaders.DATE);
+ }
+
+ /**
+ * Set Date value.
+ *
+ * @param value the value
+ */
+ public void setDate(long value) {
+ mPduHeaders.setLongInteger(value, PduHeaders.DATE);
+ }
+
+ /**
+ * Get Message-ID value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getMessageId() {
+ return mPduHeaders.getTextString(PduHeaders.MESSAGE_ID);
+ }
+
+ /**
+ * Set Message-ID value.
+ *
+ * @param value the value, should not be null
+ * @throws NullPointerException if the value is null.
+ */
+ public void setMessageId(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.MESSAGE_ID);
+ }
+
+ /**
+ * Get Status value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public int getStatus() {
+ return mPduHeaders.getOctet(PduHeaders.STATUS);
+ }
+
+ /**
+ * Set Status value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ */
+ public void setStatus(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.STATUS);
+ }
+
+ /**
+ * Get To value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public EncodedStringValue[] getTo() {
+ return mPduHeaders.getEncodedStringValues(PduHeaders.TO);
+ }
+
+ /**
+ * set To value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ public void setTo(EncodedStringValue[] value) {
+ mPduHeaders.setEncodedStringValues(value, PduHeaders.TO);
+ }
+
+ /*
+ * Optional, not supported header fields:
+ *
+ * public byte[] getApplicId() {return null;}
+ * public void setApplicId(byte[] value) {}
+ *
+ * public byte[] getAuxApplicId() {return null;}
+ * public void getAuxApplicId(byte[] value) {}
+ *
+ * public byte[] getReplyApplicId() {return 0x00;}
+ * public void setReplyApplicId(byte[] value) {}
+ *
+ * public EncodedStringValue getStatusText() {return null;}
+ * public void setStatusText(EncodedStringValue value) {}
+ */
+}
diff --git a/telephony/java/com/google/android/mms/pdu/EncodedStringValue.java b/telephony/java/com/google/android/mms/pdu/EncodedStringValue.java
new file mode 100644
index 000000000000..41662750842f
--- /dev/null
+++ b/telephony/java/com/google/android/mms/pdu/EncodedStringValue.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2007-2008 Esmertec AG.
+ * Copyright (C) 2007-2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.pdu;
+
+import android.util.Log;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+
+/**
+ * Encoded-string-value = Text-string | Value-length Char-set Text-string
+ */
+public class EncodedStringValue implements Cloneable {
+ private static final String TAG = "EncodedStringValue";
+ private static final boolean DEBUG = false;
+ private static final boolean LOCAL_LOGV = false;
+
+ /**
+ * The Char-set value.
+ */
+ private int mCharacterSet;
+
+ /**
+ * The Text-string value.
+ */
+ private byte[] mData;
+
+ /**
+ * Constructor.
+ *
+ * @param charset the Char-set value
+ * @param data the Text-string value
+ * @throws NullPointerException if Text-string value is null.
+ */
+ @UnsupportedAppUsage
+ public EncodedStringValue(int charset, byte[] data) {
+ // TODO: CharSet needs to be validated against MIBEnum.
+ if(null == data) {
+ throw new NullPointerException("EncodedStringValue: Text-string is null.");
+ }
+
+ mCharacterSet = charset;
+ mData = new byte[data.length];
+ System.arraycopy(data, 0, mData, 0, data.length);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param data the Text-string value
+ * @throws NullPointerException if Text-string value is null.
+ */
+ @UnsupportedAppUsage
+ public EncodedStringValue(byte[] data) {
+ this(CharacterSets.DEFAULT_CHARSET, data);
+ }
+
+ @UnsupportedAppUsage
+ public EncodedStringValue(String data) {
+ try {
+ mData = data.getBytes(CharacterSets.DEFAULT_CHARSET_NAME);
+ mCharacterSet = CharacterSets.DEFAULT_CHARSET;
+ } catch (UnsupportedEncodingException e) {
+ Log.e(TAG, "Default encoding must be supported.", e);
+ }
+ }
+
+ /**
+ * Get Char-set value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public int getCharacterSet() {
+ return mCharacterSet;
+ }
+
+ /**
+ * Set Char-set value.
+ *
+ * @param charset the Char-set value
+ */
+ @UnsupportedAppUsage
+ public void setCharacterSet(int charset) {
+ // TODO: CharSet needs to be validated against MIBEnum.
+ mCharacterSet = charset;
+ }
+
+ /**
+ * Get Text-string value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getTextString() {
+ byte[] byteArray = new byte[mData.length];
+
+ System.arraycopy(mData, 0, byteArray, 0, mData.length);
+ return byteArray;
+ }
+
+ /**
+ * Set Text-string value.
+ *
+ * @param textString the Text-string value
+ * @throws NullPointerException if Text-string value is null.
+ */
+ @UnsupportedAppUsage
+ public void setTextString(byte[] textString) {
+ if(null == textString) {
+ throw new NullPointerException("EncodedStringValue: Text-string is null.");
+ }
+
+ mData = new byte[textString.length];
+ System.arraycopy(textString, 0, mData, 0, textString.length);
+ }
+
+ /**
+ * Convert this object to a {@link java.lang.String}. If the encoding of
+ * the EncodedStringValue is null or unsupported, it will be
+ * treated as iso-8859-1 encoding.
+ *
+ * @return The decoded String.
+ */
+ @UnsupportedAppUsage
+ public String getString() {
+ if (CharacterSets.ANY_CHARSET == mCharacterSet) {
+ return new String(mData); // system default encoding.
+ } else {
+ try {
+ String name = CharacterSets.getMimeName(mCharacterSet);
+ return new String(mData, name);
+ } catch (UnsupportedEncodingException e) {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, e.getMessage(), e);
+ }
+ try {
+ return new String(mData, CharacterSets.MIMENAME_ISO_8859_1);
+ } catch (UnsupportedEncodingException e2) {
+ return new String(mData); // system default encoding.
+ }
+ }
+ }
+ }
+
+ /**
+ * Append to Text-string.
+ *
+ * @param textString the textString to append
+ * @throws NullPointerException if the text String is null
+ * or an IOException occurred.
+ */
+ @UnsupportedAppUsage
+ public void appendTextString(byte[] textString) {
+ if(null == textString) {
+ throw new NullPointerException("Text-string is null.");
+ }
+
+ if(null == mData) {
+ mData = new byte[textString.length];
+ System.arraycopy(textString, 0, mData, 0, textString.length);
+ } else {
+ ByteArrayOutputStream newTextString = new ByteArrayOutputStream();
+ try {
+ newTextString.write(mData);
+ newTextString.write(textString);
+ } catch (IOException e) {
+ e.printStackTrace();
+ throw new NullPointerException(
+ "appendTextString: failed when write a new Text-string");
+ }
+
+ mData = newTextString.toByteArray();
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.lang.Object#clone()
+ */
+ @Override
+ public Object clone() throws CloneNotSupportedException {
+ super.clone();
+ int len = mData.length;
+ byte[] dstBytes = new byte[len];
+ System.arraycopy(mData, 0, dstBytes, 0, len);
+
+ try {
+ return new EncodedStringValue(mCharacterSet, dstBytes);
+ } catch (Exception e) {
+ Log.e(TAG, "failed to clone an EncodedStringValue: " + this);
+ e.printStackTrace();
+ throw new CloneNotSupportedException(e.getMessage());
+ }
+ }
+
+ /**
+ * Split this encoded string around matches of the given pattern.
+ *
+ * @param pattern the delimiting pattern
+ * @return the array of encoded strings computed by splitting this encoded
+ * string around matches of the given pattern
+ */
+ public EncodedStringValue[] split(String pattern) {
+ String[] temp = getString().split(pattern);
+ EncodedStringValue[] ret = new EncodedStringValue[temp.length];
+ for (int i = 0; i < ret.length; ++i) {
+ try {
+ ret[i] = new EncodedStringValue(mCharacterSet,
+ temp[i].getBytes());
+ } catch (NullPointerException e) {
+ // Can't arrive here
+ return null;
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * Extract an EncodedStringValue[] from a given String.
+ */
+ @UnsupportedAppUsage
+ public static EncodedStringValue[] extract(String src) {
+ String[] values = src.split(";");
+
+ ArrayList<EncodedStringValue> list = new ArrayList<EncodedStringValue>();
+ for (int i = 0; i < values.length; i++) {
+ if (values[i].length() > 0) {
+ list.add(new EncodedStringValue(values[i]));
+ }
+ }
+
+ int len = list.size();
+ if (len > 0) {
+ return list.toArray(new EncodedStringValue[len]);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Concatenate an EncodedStringValue[] into a single String.
+ */
+ @UnsupportedAppUsage
+ public static String concat(EncodedStringValue[] addr) {
+ StringBuilder sb = new StringBuilder();
+ int maxIndex = addr.length - 1;
+ for (int i = 0; i <= maxIndex; i++) {
+ sb.append(addr[i].getString());
+ if (i < maxIndex) {
+ sb.append(";");
+ }
+ }
+
+ return sb.toString();
+ }
+
+ @UnsupportedAppUsage
+ public static EncodedStringValue copy(EncodedStringValue value) {
+ if (value == null) {
+ return null;
+ }
+
+ return new EncodedStringValue(value.mCharacterSet, value.mData);
+ }
+
+ @UnsupportedAppUsage
+ public static EncodedStringValue[] encodeStrings(String[] array) {
+ int count = array.length;
+ if (count > 0) {
+ EncodedStringValue[] encodedArray = new EncodedStringValue[count];
+ for (int i = 0; i < count; i++) {
+ encodedArray[i] = new EncodedStringValue(array[i]);
+ }
+ return encodedArray;
+ }
+ return null;
+ }
+}
diff --git a/telephony/java/com/google/android/mms/pdu/GenericPdu.java b/telephony/java/com/google/android/mms/pdu/GenericPdu.java
new file mode 100644
index 000000000000..ebf16ac7e632
--- /dev/null
+++ b/telephony/java/com/google/android/mms/pdu/GenericPdu.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import com.google.android.mms.InvalidHeaderValueException;
+
+public class GenericPdu {
+ /**
+ * The headers of pdu.
+ */
+ @UnsupportedAppUsage
+ PduHeaders mPduHeaders = null;
+
+ /**
+ * Constructor.
+ */
+ @UnsupportedAppUsage
+ public GenericPdu() {
+ mPduHeaders = new PduHeaders();
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param headers Headers for this PDU.
+ */
+ GenericPdu(PduHeaders headers) {
+ mPduHeaders = headers;
+ }
+
+ /**
+ * Get the headers of this PDU.
+ *
+ * @return A PduHeaders of this PDU.
+ */
+ @UnsupportedAppUsage
+ PduHeaders getPduHeaders() {
+ return mPduHeaders;
+ }
+
+ /**
+ * Get X-Mms-Message-Type field value.
+ *
+ * @return the X-Mms-Report-Allowed value
+ */
+ @UnsupportedAppUsage
+ public int getMessageType() {
+ return mPduHeaders.getOctet(PduHeaders.MESSAGE_TYPE);
+ }
+
+ /**
+ * Set X-Mms-Message-Type field value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ * RuntimeException if field's value is not Octet.
+ */
+ @UnsupportedAppUsage
+ public void setMessageType(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.MESSAGE_TYPE);
+ }
+
+ /**
+ * Get X-Mms-MMS-Version field value.
+ *
+ * @return the X-Mms-MMS-Version value
+ */
+ public int getMmsVersion() {
+ return mPduHeaders.getOctet(PduHeaders.MMS_VERSION);
+ }
+
+ /**
+ * Set X-Mms-MMS-Version field value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ * RuntimeException if field's value is not Octet.
+ */
+ public void setMmsVersion(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.MMS_VERSION);
+ }
+
+ /**
+ * Get From value.
+ * From-value = Value-length
+ * (Address-present-token Encoded-string-value | Insert-address-token)
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public EncodedStringValue getFrom() {
+ return mPduHeaders.getEncodedStringValue(PduHeaders.FROM);
+ }
+
+ /**
+ * Set From value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setFrom(EncodedStringValue value) {
+ mPduHeaders.setEncodedStringValue(value, PduHeaders.FROM);
+ }
+}
diff --git a/telephony/java/com/google/android/mms/pdu/MultimediaMessagePdu.java b/telephony/java/com/google/android/mms/pdu/MultimediaMessagePdu.java
new file mode 100644
index 000000000000..e108f7600baf
--- /dev/null
+++ b/telephony/java/com/google/android/mms/pdu/MultimediaMessagePdu.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import com.google.android.mms.InvalidHeaderValueException;
+
+/**
+ * Multimedia message PDU.
+ */
+public class MultimediaMessagePdu extends GenericPdu{
+ /**
+ * The body.
+ */
+ private PduBody mMessageBody;
+
+ /**
+ * Constructor.
+ */
+ @UnsupportedAppUsage
+ public MultimediaMessagePdu() {
+ super();
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param header the header of this PDU
+ * @param body the body of this PDU
+ */
+ @UnsupportedAppUsage
+ public MultimediaMessagePdu(PduHeaders header, PduBody body) {
+ super(header);
+ mMessageBody = body;
+ }
+
+ /**
+ * Constructor with given headers.
+ *
+ * @param headers Headers for this PDU.
+ */
+ MultimediaMessagePdu(PduHeaders headers) {
+ super(headers);
+ }
+
+ /**
+ * Get body of the PDU.
+ *
+ * @return the body
+ */
+ @UnsupportedAppUsage
+ public PduBody getBody() {
+ return mMessageBody;
+ }
+
+ /**
+ * Set body of the PDU.
+ *
+ * @param body the body
+ */
+ @UnsupportedAppUsage
+ public void setBody(PduBody body) {
+ mMessageBody = body;
+ }
+
+ /**
+ * Get subject.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public EncodedStringValue getSubject() {
+ return mPduHeaders.getEncodedStringValue(PduHeaders.SUBJECT);
+ }
+
+ /**
+ * Set subject.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setSubject(EncodedStringValue value) {
+ mPduHeaders.setEncodedStringValue(value, PduHeaders.SUBJECT);
+ }
+
+ /**
+ * Get To value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public EncodedStringValue[] getTo() {
+ return mPduHeaders.getEncodedStringValues(PduHeaders.TO);
+ }
+
+ /**
+ * Add a "To" value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void addTo(EncodedStringValue value) {
+ mPduHeaders.appendEncodedStringValue(value, PduHeaders.TO);
+ }
+
+ /**
+ * Get X-Mms-Priority value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public int getPriority() {
+ return mPduHeaders.getOctet(PduHeaders.PRIORITY);
+ }
+
+ /**
+ * Set X-Mms-Priority value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ */
+ @UnsupportedAppUsage
+ public void setPriority(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.PRIORITY);
+ }
+
+ /**
+ * Get Date value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public long getDate() {
+ return mPduHeaders.getLongInteger(PduHeaders.DATE);
+ }
+
+ /**
+ * Set Date value in seconds.
+ *
+ * @param value the value
+ */
+ @UnsupportedAppUsage
+ public void setDate(long value) {
+ mPduHeaders.setLongInteger(value, PduHeaders.DATE);
+ }
+}
diff --git a/telephony/java/com/google/android/mms/pdu/NotificationInd.java b/telephony/java/com/google/android/mms/pdu/NotificationInd.java
new file mode 100644
index 000000000000..b561bd4ab3a7
--- /dev/null
+++ b/telephony/java/com/google/android/mms/pdu/NotificationInd.java
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import com.google.android.mms.InvalidHeaderValueException;
+
+/**
+ * M-Notification.ind PDU.
+ */
+public class NotificationInd extends GenericPdu {
+ /**
+ * Empty constructor.
+ * Since the Pdu corresponding to this class is constructed
+ * by the Proxy-Relay server, this class is only instantiated
+ * by the Pdu Parser.
+ *
+ * @throws InvalidHeaderValueException if error occurs.
+ * RuntimeException if an undeclared error occurs.
+ */
+ @UnsupportedAppUsage
+ public NotificationInd() throws InvalidHeaderValueException {
+ super();
+ setMessageType(PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND);
+ }
+
+ /**
+ * Constructor with given headers.
+ *
+ * @param headers Headers for this PDU.
+ */
+ @UnsupportedAppUsage
+ NotificationInd(PduHeaders headers) {
+ super(headers);
+ }
+
+ /**
+ * Get X-Mms-Content-Class Value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public int getContentClass() {
+ return mPduHeaders.getOctet(PduHeaders.CONTENT_CLASS);
+ }
+
+ /**
+ * Set X-Mms-Content-Class Value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ * RuntimeException if an undeclared error occurs.
+ */
+ @UnsupportedAppUsage
+ public void setContentClass(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.CONTENT_CLASS);
+ }
+
+ /**
+ * Get X-Mms-Content-Location value.
+ * When used in a PDU other than M-Mbox-Delete.conf and M-Delete.conf:
+ * Content-location-value = Uri-value
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getContentLocation() {
+ return mPduHeaders.getTextString(PduHeaders.CONTENT_LOCATION);
+ }
+
+ /**
+ * Set X-Mms-Content-Location value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ * RuntimeException if an undeclared error occurs.
+ */
+ @UnsupportedAppUsage
+ public void setContentLocation(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.CONTENT_LOCATION);
+ }
+
+ /**
+ * Get X-Mms-Expiry value.
+ *
+ * Expiry-value = Value-length
+ * (Absolute-token Date-value | Relative-token Delta-seconds-value)
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public long getExpiry() {
+ return mPduHeaders.getLongInteger(PduHeaders.EXPIRY);
+ }
+
+ /**
+ * Set X-Mms-Expiry value.
+ *
+ * @param value the value
+ * @throws RuntimeException if an undeclared error occurs.
+ */
+ @UnsupportedAppUsage
+ public void setExpiry(long value) {
+ mPduHeaders.setLongInteger(value, PduHeaders.EXPIRY);
+ }
+
+ /**
+ * Get From value.
+ * From-value = Value-length
+ * (Address-present-token Encoded-string-value | Insert-address-token)
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public EncodedStringValue getFrom() {
+ return mPduHeaders.getEncodedStringValue(PduHeaders.FROM);
+ }
+
+ /**
+ * Set From value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ * RuntimeException if an undeclared error occurs.
+ */
+ @UnsupportedAppUsage
+ public void setFrom(EncodedStringValue value) {
+ mPduHeaders.setEncodedStringValue(value, PduHeaders.FROM);
+ }
+
+ /**
+ * Get X-Mms-Message-Class value.
+ * Message-class-value = Class-identifier | Token-text
+ * Class-identifier = Personal | Advertisement | Informational | Auto
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getMessageClass() {
+ return mPduHeaders.getTextString(PduHeaders.MESSAGE_CLASS);
+ }
+
+ /**
+ * Set X-Mms-Message-Class value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ * RuntimeException if an undeclared error occurs.
+ */
+ @UnsupportedAppUsage
+ public void setMessageClass(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.MESSAGE_CLASS);
+ }
+
+ /**
+ * Get X-Mms-Message-Size value.
+ * Message-size-value = Long-integer
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public long getMessageSize() {
+ return mPduHeaders.getLongInteger(PduHeaders.MESSAGE_SIZE);
+ }
+
+ /**
+ * Set X-Mms-Message-Size value.
+ *
+ * @param value the value
+ * @throws RuntimeException if an undeclared error occurs.
+ */
+ @UnsupportedAppUsage
+ public void setMessageSize(long value) {
+ mPduHeaders.setLongInteger(value, PduHeaders.MESSAGE_SIZE);
+ }
+
+ /**
+ * Get subject.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public EncodedStringValue getSubject() {
+ return mPduHeaders.getEncodedStringValue(PduHeaders.SUBJECT);
+ }
+
+ /**
+ * Set subject.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ * RuntimeException if an undeclared error occurs.
+ */
+ @UnsupportedAppUsage
+ public void setSubject(EncodedStringValue value) {
+ mPduHeaders.setEncodedStringValue(value, PduHeaders.SUBJECT);
+ }
+
+ /**
+ * Get X-Mms-Transaction-Id.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getTransactionId() {
+ return mPduHeaders.getTextString(PduHeaders.TRANSACTION_ID);
+ }
+
+ /**
+ * Set X-Mms-Transaction-Id.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ * RuntimeException if an undeclared error occurs.
+ */
+ @UnsupportedAppUsage
+ public void setTransactionId(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.TRANSACTION_ID);
+ }
+
+ /**
+ * Get X-Mms-Delivery-Report Value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public int getDeliveryReport() {
+ return mPduHeaders.getOctet(PduHeaders.DELIVERY_REPORT);
+ }
+
+ /**
+ * Set X-Mms-Delivery-Report Value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ * RuntimeException if an undeclared error occurs.
+ */
+ @UnsupportedAppUsage
+ public void setDeliveryReport(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.DELIVERY_REPORT);
+ }
+
+ /*
+ * Optional, not supported header fields:
+ *
+ * public byte[] getApplicId() {return null;}
+ * public void setApplicId(byte[] value) {}
+ *
+ * public byte[] getAuxApplicId() {return null;}
+ * public void getAuxApplicId(byte[] value) {}
+ *
+ * public byte getDrmContent() {return 0x00;}
+ * public void setDrmContent(byte value) {}
+ *
+ * public byte getDistributionIndicator() {return 0x00;}
+ * public void setDistributionIndicator(byte value) {}
+ *
+ * public ElementDescriptorValue getElementDescriptor() {return null;}
+ * public void getElementDescriptor(ElementDescriptorValue value) {}
+ *
+ * public byte getPriority() {return 0x00;}
+ * public void setPriority(byte value) {}
+ *
+ * public byte getRecommendedRetrievalMode() {return 0x00;}
+ * public void setRecommendedRetrievalMode(byte value) {}
+ *
+ * public byte getRecommendedRetrievalModeText() {return 0x00;}
+ * public void setRecommendedRetrievalModeText(byte value) {}
+ *
+ * public byte[] getReplaceId() {return 0x00;}
+ * public void setReplaceId(byte[] value) {}
+ *
+ * public byte[] getReplyApplicId() {return 0x00;}
+ * public void setReplyApplicId(byte[] value) {}
+ *
+ * public byte getReplyCharging() {return 0x00;}
+ * public void setReplyCharging(byte value) {}
+ *
+ * public byte getReplyChargingDeadline() {return 0x00;}
+ * public void setReplyChargingDeadline(byte value) {}
+ *
+ * public byte[] getReplyChargingId() {return 0x00;}
+ * public void setReplyChargingId(byte[] value) {}
+ *
+ * public long getReplyChargingSize() {return 0;}
+ * public void setReplyChargingSize(long value) {}
+ *
+ * public byte getStored() {return 0x00;}
+ * public void setStored(byte value) {}
+ */
+}
diff --git a/telephony/java/com/google/android/mms/pdu/NotifyRespInd.java b/telephony/java/com/google/android/mms/pdu/NotifyRespInd.java
new file mode 100644
index 000000000000..3c70f86a0890
--- /dev/null
+++ b/telephony/java/com/google/android/mms/pdu/NotifyRespInd.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import com.google.android.mms.InvalidHeaderValueException;
+
+/**
+ * M-NofifyResp.ind PDU.
+ */
+public class NotifyRespInd extends GenericPdu {
+ /**
+ * Constructor, used when composing a M-NotifyResp.ind pdu.
+ *
+ * @param mmsVersion current version of mms
+ * @param transactionId the transaction-id value
+ * @param status the status value
+ * @throws InvalidHeaderValueException if parameters are invalid.
+ * NullPointerException if transactionId is null.
+ * RuntimeException if an undeclared error occurs.
+ */
+ @UnsupportedAppUsage
+ public NotifyRespInd(int mmsVersion,
+ byte[] transactionId,
+ int status) throws InvalidHeaderValueException {
+ super();
+ setMessageType(PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND);
+ setMmsVersion(mmsVersion);
+ setTransactionId(transactionId);
+ setStatus(status);
+ }
+
+ /**
+ * Constructor with given headers.
+ *
+ * @param headers Headers for this PDU.
+ */
+ @UnsupportedAppUsage
+ NotifyRespInd(PduHeaders headers) {
+ super(headers);
+ }
+
+ /**
+ * Get X-Mms-Report-Allowed field value.
+ *
+ * @return the X-Mms-Report-Allowed value
+ */
+ public int getReportAllowed() {
+ return mPduHeaders.getOctet(PduHeaders.REPORT_ALLOWED);
+ }
+
+ /**
+ * Set X-Mms-Report-Allowed field value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ * RuntimeException if an undeclared error occurs.
+ */
+ @UnsupportedAppUsage
+ public void setReportAllowed(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.REPORT_ALLOWED);
+ }
+
+ /**
+ * Set X-Mms-Status field value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ * RuntimeException if an undeclared error occurs.
+ */
+ @UnsupportedAppUsage
+ public void setStatus(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.STATUS);
+ }
+
+ /**
+ * GetX-Mms-Status field value.
+ *
+ * @return the X-Mms-Status value
+ */
+ public int getStatus() {
+ return mPduHeaders.getOctet(PduHeaders.STATUS);
+ }
+
+ /**
+ * Get X-Mms-Transaction-Id field value.
+ *
+ * @return the X-Mms-Report-Allowed value
+ */
+ public byte[] getTransactionId() {
+ return mPduHeaders.getTextString(PduHeaders.TRANSACTION_ID);
+ }
+
+ /**
+ * Set X-Mms-Transaction-Id field value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ * RuntimeException if an undeclared error occurs.
+ */
+ @UnsupportedAppUsage
+ public void setTransactionId(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.TRANSACTION_ID);
+ }
+}
diff --git a/telephony/java/com/google/android/mms/pdu/PduBody.java b/telephony/java/com/google/android/mms/pdu/PduBody.java
new file mode 100644
index 000000000000..51914e4110b0
--- /dev/null
+++ b/telephony/java/com/google/android/mms/pdu/PduBody.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Vector;
+
+public class PduBody {
+ private Vector<PduPart> mParts = null;
+
+ private Map<String, PduPart> mPartMapByContentId = null;
+ private Map<String, PduPart> mPartMapByContentLocation = null;
+ private Map<String, PduPart> mPartMapByName = null;
+ private Map<String, PduPart> mPartMapByFileName = null;
+
+ /**
+ * Constructor.
+ */
+ @UnsupportedAppUsage
+ public PduBody() {
+ mParts = new Vector<PduPart>();
+
+ mPartMapByContentId = new HashMap<String, PduPart>();
+ mPartMapByContentLocation = new HashMap<String, PduPart>();
+ mPartMapByName = new HashMap<String, PduPart>();
+ mPartMapByFileName = new HashMap<String, PduPart>();
+ }
+
+ private void putPartToMaps(PduPart part) {
+ // Put part to mPartMapByContentId.
+ byte[] contentId = part.getContentId();
+ if(null != contentId) {
+ mPartMapByContentId.put(new String(contentId), part);
+ }
+
+ // Put part to mPartMapByContentLocation.
+ byte[] contentLocation = part.getContentLocation();
+ if(null != contentLocation) {
+ String clc = new String(contentLocation);
+ mPartMapByContentLocation.put(clc, part);
+ }
+
+ // Put part to mPartMapByName.
+ byte[] name = part.getName();
+ if(null != name) {
+ String clc = new String(name);
+ mPartMapByName.put(clc, part);
+ }
+
+ // Put part to mPartMapByFileName.
+ byte[] fileName = part.getFilename();
+ if(null != fileName) {
+ String clc = new String(fileName);
+ mPartMapByFileName.put(clc, part);
+ }
+ }
+
+ /**
+ * Appends the specified part to the end of this body.
+ *
+ * @param part part to be appended
+ * @return true when success, false when fail
+ * @throws NullPointerException when part is null
+ */
+ @UnsupportedAppUsage
+ public boolean addPart(PduPart part) {
+ if(null == part) {
+ throw new NullPointerException();
+ }
+
+ putPartToMaps(part);
+ return mParts.add(part);
+ }
+
+ /**
+ * Inserts the specified part at the specified position.
+ *
+ * @param index index at which the specified part is to be inserted
+ * @param part part to be inserted
+ * @throws NullPointerException when part is null
+ */
+ @UnsupportedAppUsage
+ public void addPart(int index, PduPart part) {
+ if(null == part) {
+ throw new NullPointerException();
+ }
+
+ putPartToMaps(part);
+ mParts.add(index, part);
+ }
+
+ /**
+ * Removes the part at the specified position.
+ *
+ * @param index index of the part to return
+ * @return part at the specified index
+ */
+ @UnsupportedAppUsage
+ public PduPart removePart(int index) {
+ return mParts.remove(index);
+ }
+
+ /**
+ * Remove all of the parts.
+ */
+ public void removeAll() {
+ mParts.clear();
+ }
+
+ /**
+ * Get the part at the specified position.
+ *
+ * @param index index of the part to return
+ * @return part at the specified index
+ */
+ @UnsupportedAppUsage
+ public PduPart getPart(int index) {
+ return mParts.get(index);
+ }
+
+ /**
+ * Get the index of the specified part.
+ *
+ * @param part the part object
+ * @return index the index of the first occurrence of the part in this body
+ */
+ @UnsupportedAppUsage
+ public int getPartIndex(PduPart part) {
+ return mParts.indexOf(part);
+ }
+
+ /**
+ * Get the number of parts.
+ *
+ * @return the number of parts
+ */
+ @UnsupportedAppUsage
+ public int getPartsNum() {
+ return mParts.size();
+ }
+
+ /**
+ * Get pdu part by content id.
+ *
+ * @param cid the value of content id.
+ * @return the pdu part.
+ */
+ @UnsupportedAppUsage
+ public PduPart getPartByContentId(String cid) {
+ return mPartMapByContentId.get(cid);
+ }
+
+ /**
+ * Get pdu part by Content-Location. Content-Location of part is
+ * the same as filename and name(param of content-type).
+ *
+ * @param fileName the value of filename.
+ * @return the pdu part.
+ */
+ @UnsupportedAppUsage
+ public PduPart getPartByContentLocation(String contentLocation) {
+ return mPartMapByContentLocation.get(contentLocation);
+ }
+
+ /**
+ * Get pdu part by name.
+ *
+ * @param fileName the value of filename.
+ * @return the pdu part.
+ */
+ @UnsupportedAppUsage
+ public PduPart getPartByName(String name) {
+ return mPartMapByName.get(name);
+ }
+
+ /**
+ * Get pdu part by filename.
+ *
+ * @param fileName the value of filename.
+ * @return the pdu part.
+ */
+ @UnsupportedAppUsage
+ public PduPart getPartByFileName(String filename) {
+ return mPartMapByFileName.get(filename);
+ }
+}
diff --git a/telephony/java/com/google/android/mms/pdu/PduComposer.java b/telephony/java/com/google/android/mms/pdu/PduComposer.java
new file mode 100644
index 000000000000..e24bf21a11b5
--- /dev/null
+++ b/telephony/java/com/google/android/mms/pdu/PduComposer.java
@@ -0,0 +1,1229 @@
+/*
+ * Copyright (C) 2007-2008 Esmertec AG.
+ * Copyright (C) 2007-2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.pdu;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.text.TextUtils;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.HashMap;
+
+public class PduComposer {
+ /**
+ * Address type.
+ */
+ static private final int PDU_PHONE_NUMBER_ADDRESS_TYPE = 1;
+ static private final int PDU_EMAIL_ADDRESS_TYPE = 2;
+ static private final int PDU_IPV4_ADDRESS_TYPE = 3;
+ static private final int PDU_IPV6_ADDRESS_TYPE = 4;
+ static private final int PDU_UNKNOWN_ADDRESS_TYPE = 5;
+
+ /**
+ * Address regular expression string.
+ */
+ static final String REGEXP_PHONE_NUMBER_ADDRESS_TYPE = "\\+?[0-9|\\.|\\-]+";
+ static final String REGEXP_EMAIL_ADDRESS_TYPE = "[a-zA-Z| ]*\\<{0,1}[a-zA-Z| ]+@{1}" +
+ "[a-zA-Z| ]+\\.{1}[a-zA-Z| ]+\\>{0,1}";
+ static final String REGEXP_IPV6_ADDRESS_TYPE =
+ "[a-fA-F]{4}\\:{1}[a-fA-F0-9]{4}\\:{1}[a-fA-F0-9]{4}\\:{1}" +
+ "[a-fA-F0-9]{4}\\:{1}[a-fA-F0-9]{4}\\:{1}[a-fA-F0-9]{4}\\:{1}" +
+ "[a-fA-F0-9]{4}\\:{1}[a-fA-F0-9]{4}";
+ static final String REGEXP_IPV4_ADDRESS_TYPE = "[0-9]{1,3}\\.{1}[0-9]{1,3}\\.{1}" +
+ "[0-9]{1,3}\\.{1}[0-9]{1,3}";
+
+ /**
+ * The postfix strings of address.
+ */
+ static final String STRING_PHONE_NUMBER_ADDRESS_TYPE = "/TYPE=PLMN";
+ static final String STRING_IPV4_ADDRESS_TYPE = "/TYPE=IPV4";
+ static final String STRING_IPV6_ADDRESS_TYPE = "/TYPE=IPV6";
+
+ /**
+ * Error values.
+ */
+ static private final int PDU_COMPOSE_SUCCESS = 0;
+ static private final int PDU_COMPOSE_CONTENT_ERROR = 1;
+ static private final int PDU_COMPOSE_FIELD_NOT_SET = 2;
+ static private final int PDU_COMPOSE_FIELD_NOT_SUPPORTED = 3;
+
+ /**
+ * WAP values defined in WSP spec.
+ */
+ static private final int QUOTED_STRING_FLAG = 34;
+ static private final int END_STRING_FLAG = 0;
+ static private final int LENGTH_QUOTE = 31;
+ static private final int TEXT_MAX = 127;
+ static private final int SHORT_INTEGER_MAX = 127;
+ static private final int LONG_INTEGER_LENGTH_MAX = 8;
+
+ /**
+ * Block size when read data from InputStream.
+ */
+ static private final int PDU_COMPOSER_BLOCK_SIZE = 1024;
+
+ /**
+ * The output message.
+ */
+ @UnsupportedAppUsage
+ protected ByteArrayOutputStream mMessage = null;
+
+ /**
+ * The PDU.
+ */
+ @UnsupportedAppUsage
+ private GenericPdu mPdu = null;
+
+ /**
+ * Current visiting position of the mMessage.
+ */
+ @UnsupportedAppUsage
+ protected int mPosition = 0;
+
+ /**
+ * Message compose buffer stack.
+ */
+ @UnsupportedAppUsage
+ private BufferStack mStack = null;
+
+ /**
+ * Content resolver.
+ */
+ @UnsupportedAppUsage
+ private final ContentResolver mResolver;
+
+ /**
+ * Header of this pdu.
+ */
+ @UnsupportedAppUsage
+ private PduHeaders mPduHeader = null;
+
+ /**
+ * Map of all content type
+ */
+ @UnsupportedAppUsage
+ private static HashMap<String, Integer> mContentTypeMap = null;
+
+ static {
+ mContentTypeMap = new HashMap<String, Integer>();
+
+ int i;
+ for (i = 0; i < PduContentTypes.contentTypes.length; i++) {
+ mContentTypeMap.put(PduContentTypes.contentTypes[i], i);
+ }
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param context the context
+ * @param pdu the pdu to be composed
+ */
+ @UnsupportedAppUsage
+ public PduComposer(Context context, GenericPdu pdu) {
+ mPdu = pdu;
+ mResolver = context.getContentResolver();
+ mPduHeader = pdu.getPduHeaders();
+ mStack = new BufferStack();
+ mMessage = new ByteArrayOutputStream();
+ mPosition = 0;
+ }
+
+ /**
+ * Make the message. No need to check whether mandatory fields are set,
+ * because the constructors of outgoing pdus are taking care of this.
+ *
+ * @return OutputStream of maked message. Return null if
+ * the PDU is invalid.
+ */
+ @UnsupportedAppUsage
+ public byte[] make() {
+ // Get Message-type.
+ int type = mPdu.getMessageType();
+
+ /* make the message */
+ switch (type) {
+ case PduHeaders.MESSAGE_TYPE_SEND_REQ:
+ case PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF:
+ if (makeSendRetrievePdu(type) != PDU_COMPOSE_SUCCESS) {
+ return null;
+ }
+ break;
+ case PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND:
+ if (makeNotifyResp() != PDU_COMPOSE_SUCCESS) {
+ return null;
+ }
+ break;
+ case PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND:
+ if (makeAckInd() != PDU_COMPOSE_SUCCESS) {
+ return null;
+ }
+ break;
+ case PduHeaders.MESSAGE_TYPE_READ_REC_IND:
+ if (makeReadRecInd() != PDU_COMPOSE_SUCCESS) {
+ return null;
+ }
+ break;
+ default:
+ return null;
+ }
+
+ return mMessage.toByteArray();
+ }
+
+ /**
+ * Copy buf to mMessage.
+ */
+ @UnsupportedAppUsage
+ protected void arraycopy(byte[] buf, int pos, int length) {
+ mMessage.write(buf, pos, length);
+ mPosition = mPosition + length;
+ }
+
+ /**
+ * Append a byte to mMessage.
+ */
+ protected void append(int value) {
+ mMessage.write(value);
+ mPosition ++;
+ }
+
+ /**
+ * Append short integer value to mMessage.
+ * This implementation doesn't check the validity of parameter, since it
+ * assumes that the values are validated in the GenericPdu setter methods.
+ */
+ @UnsupportedAppUsage
+ protected void appendShortInteger(int value) {
+ /*
+ * From WAP-230-WSP-20010705-a:
+ * Short-integer = OCTET
+ * ; Integers in range 0-127 shall be encoded as a one octet value
+ * ; with the most significant bit set to one (1xxx xxxx) and with
+ * ; the value in the remaining least significant bits.
+ * In our implementation, only low 7 bits are stored and otherwise
+ * bits are ignored.
+ */
+ append((value | 0x80) & 0xff);
+ }
+
+ /**
+ * Append an octet number between 128 and 255 into mMessage.
+ * NOTE:
+ * A value between 0 and 127 should be appended by using appendShortInteger.
+ * This implementation doesn't check the validity of parameter, since it
+ * assumes that the values are validated in the GenericPdu setter methods.
+ */
+ @UnsupportedAppUsage
+ protected void appendOctet(int number) {
+ append(number);
+ }
+
+ /**
+ * Append a short length into mMessage.
+ * This implementation doesn't check the validity of parameter, since it
+ * assumes that the values are validated in the GenericPdu setter methods.
+ */
+ protected void appendShortLength(int value) {
+ /*
+ * From WAP-230-WSP-20010705-a:
+ * Short-length = <Any octet 0-30>
+ */
+ append(value);
+ }
+
+ /**
+ * Append long integer into mMessage. it's used for really long integers.
+ * This implementation doesn't check the validity of parameter, since it
+ * assumes that the values are validated in the GenericPdu setter methods.
+ */
+ @UnsupportedAppUsage
+ protected void appendLongInteger(long longInt) {
+ /*
+ * From WAP-230-WSP-20010705-a:
+ * Long-integer = Short-length Multi-octet-integer
+ * ; The Short-length indicates the length of the Multi-octet-integer
+ * Multi-octet-integer = 1*30 OCTET
+ * ; The content octets shall be an unsigned integer value with the
+ * ; most significant octet encoded first (big-endian representation).
+ * ; The minimum number of octets must be used to encode the value.
+ */
+ int size;
+ long temp = longInt;
+
+ // Count the length of the long integer.
+ for(size = 0; (temp != 0) && (size < LONG_INTEGER_LENGTH_MAX); size++) {
+ temp = (temp >>> 8);
+ }
+
+ // Set Length.
+ appendShortLength(size);
+
+ // Count and set the long integer.
+ int i;
+ int shift = (size -1) * 8;
+
+ for (i = 0; i < size; i++) {
+ append((int)((longInt >>> shift) & 0xff));
+ shift = shift - 8;
+ }
+ }
+
+ /**
+ * Append text string into mMessage.
+ * This implementation doesn't check the validity of parameter, since it
+ * assumes that the values are validated in the GenericPdu setter methods.
+ */
+ @UnsupportedAppUsage
+ protected void appendTextString(byte[] text) {
+ /*
+ * From WAP-230-WSP-20010705-a:
+ * Text-string = [Quote] *TEXT End-of-string
+ * ; If the first character in the TEXT is in the range of 128-255,
+ * ; a Quote character must precede it. Otherwise the Quote character
+ * ;must be omitted. The Quote is not part of the contents.
+ */
+ if (((text[0])&0xff) > TEXT_MAX) { // No need to check for <= 255
+ append(TEXT_MAX);
+ }
+
+ arraycopy(text, 0, text.length);
+ append(0);
+ }
+
+ /**
+ * Append text string into mMessage.
+ * This implementation doesn't check the validity of parameter, since it
+ * assumes that the values are validated in the GenericPdu setter methods.
+ */
+ @UnsupportedAppUsage
+ protected void appendTextString(String str) {
+ /*
+ * From WAP-230-WSP-20010705-a:
+ * Text-string = [Quote] *TEXT End-of-string
+ * ; If the first character in the TEXT is in the range of 128-255,
+ * ; a Quote character must precede it. Otherwise the Quote character
+ * ;must be omitted. The Quote is not part of the contents.
+ */
+ appendTextString(str.getBytes());
+ }
+
+ /**
+ * Append encoded string value to mMessage.
+ * This implementation doesn't check the validity of parameter, since it
+ * assumes that the values are validated in the GenericPdu setter methods.
+ */
+ @UnsupportedAppUsage
+ protected void appendEncodedString(EncodedStringValue enStr) {
+ /*
+ * From OMA-TS-MMS-ENC-V1_3-20050927-C:
+ * Encoded-string-value = Text-string | Value-length Char-set Text-string
+ */
+ assert(enStr != null);
+
+ int charset = enStr.getCharacterSet();
+ byte[] textString = enStr.getTextString();
+ if (null == textString) {
+ return;
+ }
+
+ /*
+ * In the implementation of EncodedStringValue, the charset field will
+ * never be 0. It will always be composed as
+ * Encoded-string-value = Value-length Char-set Text-string
+ */
+ mStack.newbuf();
+ PositionMarker start = mStack.mark();
+
+ appendShortInteger(charset);
+ appendTextString(textString);
+
+ int len = start.getLength();
+ mStack.pop();
+ appendValueLength(len);
+ mStack.copy();
+ }
+
+ /**
+ * Append uintvar integer into mMessage.
+ * This implementation doesn't check the validity of parameter, since it
+ * assumes that the values are validated in the GenericPdu setter methods.
+ */
+ @UnsupportedAppUsage
+ protected void appendUintvarInteger(long value) {
+ /*
+ * From WAP-230-WSP-20010705-a:
+ * To encode a large unsigned integer, split it into 7-bit fragments
+ * and place them in the payloads of multiple octets. The most significant
+ * bits are placed in the first octets with the least significant bits
+ * ending up in the last octet. All octets MUST set the Continue bit to 1
+ * except the last octet, which MUST set the Continue bit to 0.
+ */
+ int i;
+ long max = SHORT_INTEGER_MAX;
+
+ for (i = 0; i < 5; i++) {
+ if (value < max) {
+ break;
+ }
+
+ max = (max << 7) | 0x7fl;
+ }
+
+ while(i > 0) {
+ long temp = value >>> (i * 7);
+ temp = temp & 0x7f;
+
+ append((int)((temp | 0x80) & 0xff));
+
+ i--;
+ }
+
+ append((int)(value & 0x7f));
+ }
+
+ /**
+ * Append date value into mMessage.
+ * This implementation doesn't check the validity of parameter, since it
+ * assumes that the values are validated in the GenericPdu setter methods.
+ */
+ protected void appendDateValue(long date) {
+ /*
+ * From OMA-TS-MMS-ENC-V1_3-20050927-C:
+ * Date-value = Long-integer
+ */
+ appendLongInteger(date);
+ }
+
+ /**
+ * Append value length to mMessage.
+ * This implementation doesn't check the validity of parameter, since it
+ * assumes that the values are validated in the GenericPdu setter methods.
+ */
+ @UnsupportedAppUsage
+ protected void appendValueLength(long value) {
+ /*
+ * From WAP-230-WSP-20010705-a:
+ * Value-length = Short-length | (Length-quote Length)
+ * ; Value length is used to indicate the length of the value to follow
+ * Short-length = <Any octet 0-30>
+ * Length-quote = <Octet 31>
+ * Length = Uintvar-integer
+ */
+ if (value < LENGTH_QUOTE) {
+ appendShortLength((int) value);
+ return;
+ }
+
+ append(LENGTH_QUOTE);
+ appendUintvarInteger(value);
+ }
+
+ /**
+ * Append quoted string to mMessage.
+ * This implementation doesn't check the validity of parameter, since it
+ * assumes that the values are validated in the GenericPdu setter methods.
+ */
+ @UnsupportedAppUsage
+ protected void appendQuotedString(byte[] text) {
+ /*
+ * From WAP-230-WSP-20010705-a:
+ * Quoted-string = <Octet 34> *TEXT End-of-string
+ * ;The TEXT encodes an RFC2616 Quoted-string with the enclosing
+ * ;quotation-marks <"> removed.
+ */
+ append(QUOTED_STRING_FLAG);
+ arraycopy(text, 0, text.length);
+ append(END_STRING_FLAG);
+ }
+
+ /**
+ * Append quoted string to mMessage.
+ * This implementation doesn't check the validity of parameter, since it
+ * assumes that the values are validated in the GenericPdu setter methods.
+ */
+ @UnsupportedAppUsage
+ protected void appendQuotedString(String str) {
+ /*
+ * From WAP-230-WSP-20010705-a:
+ * Quoted-string = <Octet 34> *TEXT End-of-string
+ * ;The TEXT encodes an RFC2616 Quoted-string with the enclosing
+ * ;quotation-marks <"> removed.
+ */
+ appendQuotedString(str.getBytes());
+ }
+
+ private EncodedStringValue appendAddressType(EncodedStringValue address) {
+ EncodedStringValue temp = null;
+
+ try {
+ int addressType = checkAddressType(address.getString());
+ temp = EncodedStringValue.copy(address);
+ if (PDU_PHONE_NUMBER_ADDRESS_TYPE == addressType) {
+ // Phone number.
+ temp.appendTextString(STRING_PHONE_NUMBER_ADDRESS_TYPE.getBytes());
+ } else if (PDU_IPV4_ADDRESS_TYPE == addressType) {
+ // Ipv4 address.
+ temp.appendTextString(STRING_IPV4_ADDRESS_TYPE.getBytes());
+ } else if (PDU_IPV6_ADDRESS_TYPE == addressType) {
+ // Ipv6 address.
+ temp.appendTextString(STRING_IPV6_ADDRESS_TYPE.getBytes());
+ }
+ } catch (NullPointerException e) {
+ return null;
+ }
+
+ return temp;
+ }
+
+ /**
+ * Append header to mMessage.
+ */
+ @UnsupportedAppUsage
+ private int appendHeader(int field) {
+ switch (field) {
+ case PduHeaders.MMS_VERSION:
+ appendOctet(field);
+
+ int version = mPduHeader.getOctet(field);
+ if (0 == version) {
+ appendShortInteger(PduHeaders.CURRENT_MMS_VERSION);
+ } else {
+ appendShortInteger(version);
+ }
+
+ break;
+
+ case PduHeaders.MESSAGE_ID:
+ case PduHeaders.TRANSACTION_ID:
+ byte[] textString = mPduHeader.getTextString(field);
+ if (null == textString) {
+ return PDU_COMPOSE_FIELD_NOT_SET;
+ }
+
+ appendOctet(field);
+ appendTextString(textString);
+ break;
+
+ case PduHeaders.TO:
+ case PduHeaders.BCC:
+ case PduHeaders.CC:
+ EncodedStringValue[] addr = mPduHeader.getEncodedStringValues(field);
+
+ if (null == addr) {
+ return PDU_COMPOSE_FIELD_NOT_SET;
+ }
+
+ EncodedStringValue temp;
+ for (int i = 0; i < addr.length; i++) {
+ temp = appendAddressType(addr[i]);
+ if (temp == null) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ appendOctet(field);
+ appendEncodedString(temp);
+ }
+ break;
+
+ case PduHeaders.FROM:
+ // Value-length (Address-present-token Encoded-string-value | Insert-address-token)
+ appendOctet(field);
+
+ EncodedStringValue from = mPduHeader.getEncodedStringValue(field);
+ if ((from == null)
+ || TextUtils.isEmpty(from.getString())
+ || new String(from.getTextString()).equals(
+ PduHeaders.FROM_INSERT_ADDRESS_TOKEN_STR)) {
+ // Length of from = 1
+ append(1);
+ // Insert-address-token = <Octet 129>
+ append(PduHeaders.FROM_INSERT_ADDRESS_TOKEN);
+ } else {
+ mStack.newbuf();
+ PositionMarker fstart = mStack.mark();
+
+ // Address-present-token = <Octet 128>
+ append(PduHeaders.FROM_ADDRESS_PRESENT_TOKEN);
+
+ temp = appendAddressType(from);
+ if (temp == null) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ appendEncodedString(temp);
+
+ int flen = fstart.getLength();
+ mStack.pop();
+ appendValueLength(flen);
+ mStack.copy();
+ }
+ break;
+
+ case PduHeaders.READ_STATUS:
+ case PduHeaders.STATUS:
+ case PduHeaders.REPORT_ALLOWED:
+ case PduHeaders.PRIORITY:
+ case PduHeaders.DELIVERY_REPORT:
+ case PduHeaders.READ_REPORT:
+ case PduHeaders.RETRIEVE_STATUS:
+ int octet = mPduHeader.getOctet(field);
+ if (0 == octet) {
+ return PDU_COMPOSE_FIELD_NOT_SET;
+ }
+
+ appendOctet(field);
+ appendOctet(octet);
+ break;
+
+ case PduHeaders.DATE:
+ long date = mPduHeader.getLongInteger(field);
+ if (-1 == date) {
+ return PDU_COMPOSE_FIELD_NOT_SET;
+ }
+
+ appendOctet(field);
+ appendDateValue(date);
+ break;
+
+ case PduHeaders.SUBJECT:
+ case PduHeaders.RETRIEVE_TEXT:
+ EncodedStringValue enString =
+ mPduHeader.getEncodedStringValue(field);
+ if (null == enString) {
+ return PDU_COMPOSE_FIELD_NOT_SET;
+ }
+
+ appendOctet(field);
+ appendEncodedString(enString);
+ break;
+
+ case PduHeaders.MESSAGE_CLASS:
+ byte[] messageClass = mPduHeader.getTextString(field);
+ if (null == messageClass) {
+ return PDU_COMPOSE_FIELD_NOT_SET;
+ }
+
+ appendOctet(field);
+ if (Arrays.equals(messageClass,
+ PduHeaders.MESSAGE_CLASS_ADVERTISEMENT_STR.getBytes())) {
+ appendOctet(PduHeaders.MESSAGE_CLASS_ADVERTISEMENT);
+ } else if (Arrays.equals(messageClass,
+ PduHeaders.MESSAGE_CLASS_AUTO_STR.getBytes())) {
+ appendOctet(PduHeaders.MESSAGE_CLASS_AUTO);
+ } else if (Arrays.equals(messageClass,
+ PduHeaders.MESSAGE_CLASS_PERSONAL_STR.getBytes())) {
+ appendOctet(PduHeaders.MESSAGE_CLASS_PERSONAL);
+ } else if (Arrays.equals(messageClass,
+ PduHeaders.MESSAGE_CLASS_INFORMATIONAL_STR.getBytes())) {
+ appendOctet(PduHeaders.MESSAGE_CLASS_INFORMATIONAL);
+ } else {
+ appendTextString(messageClass);
+ }
+ break;
+
+ case PduHeaders.EXPIRY:
+ long expiry = mPduHeader.getLongInteger(field);
+ if (-1 == expiry) {
+ return PDU_COMPOSE_FIELD_NOT_SET;
+ }
+
+ appendOctet(field);
+
+ mStack.newbuf();
+ PositionMarker expiryStart = mStack.mark();
+
+ append(PduHeaders.VALUE_RELATIVE_TOKEN);
+ appendLongInteger(expiry);
+
+ int expiryLength = expiryStart.getLength();
+ mStack.pop();
+ appendValueLength(expiryLength);
+ mStack.copy();
+ break;
+
+ default:
+ return PDU_COMPOSE_FIELD_NOT_SUPPORTED;
+ }
+
+ return PDU_COMPOSE_SUCCESS;
+ }
+
+ /**
+ * Make ReadRec.Ind.
+ */
+ private int makeReadRecInd() {
+ if (mMessage == null) {
+ mMessage = new ByteArrayOutputStream();
+ mPosition = 0;
+ }
+
+ // X-Mms-Message-Type
+ appendOctet(PduHeaders.MESSAGE_TYPE);
+ appendOctet(PduHeaders.MESSAGE_TYPE_READ_REC_IND);
+
+ // X-Mms-MMS-Version
+ if (appendHeader(PduHeaders.MMS_VERSION) != PDU_COMPOSE_SUCCESS) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ // Message-ID
+ if (appendHeader(PduHeaders.MESSAGE_ID) != PDU_COMPOSE_SUCCESS) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ // To
+ if (appendHeader(PduHeaders.TO) != PDU_COMPOSE_SUCCESS) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ // From
+ if (appendHeader(PduHeaders.FROM) != PDU_COMPOSE_SUCCESS) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ // Date Optional
+ appendHeader(PduHeaders.DATE);
+
+ // X-Mms-Read-Status
+ if (appendHeader(PduHeaders.READ_STATUS) != PDU_COMPOSE_SUCCESS) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ // X-Mms-Applic-ID Optional(not support)
+ // X-Mms-Reply-Applic-ID Optional(not support)
+ // X-Mms-Aux-Applic-Info Optional(not support)
+
+ return PDU_COMPOSE_SUCCESS;
+ }
+
+ /**
+ * Make NotifyResp.Ind.
+ */
+ private int makeNotifyResp() {
+ if (mMessage == null) {
+ mMessage = new ByteArrayOutputStream();
+ mPosition = 0;
+ }
+
+ // X-Mms-Message-Type
+ appendOctet(PduHeaders.MESSAGE_TYPE);
+ appendOctet(PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND);
+
+ // X-Mms-Transaction-ID
+ if (appendHeader(PduHeaders.TRANSACTION_ID) != PDU_COMPOSE_SUCCESS) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ // X-Mms-MMS-Version
+ if (appendHeader(PduHeaders.MMS_VERSION) != PDU_COMPOSE_SUCCESS) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ // X-Mms-Status
+ if (appendHeader(PduHeaders.STATUS) != PDU_COMPOSE_SUCCESS) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ // X-Mms-Report-Allowed Optional (not support)
+ return PDU_COMPOSE_SUCCESS;
+ }
+
+ /**
+ * Make Acknowledge.Ind.
+ */
+ private int makeAckInd() {
+ if (mMessage == null) {
+ mMessage = new ByteArrayOutputStream();
+ mPosition = 0;
+ }
+
+ // X-Mms-Message-Type
+ appendOctet(PduHeaders.MESSAGE_TYPE);
+ appendOctet(PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND);
+
+ // X-Mms-Transaction-ID
+ if (appendHeader(PduHeaders.TRANSACTION_ID) != PDU_COMPOSE_SUCCESS) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ // X-Mms-MMS-Version
+ if (appendHeader(PduHeaders.MMS_VERSION) != PDU_COMPOSE_SUCCESS) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ // X-Mms-Report-Allowed Optional
+ appendHeader(PduHeaders.REPORT_ALLOWED);
+
+ return PDU_COMPOSE_SUCCESS;
+ }
+
+ /**
+ * Make Send.req.
+ */
+ private int makeSendRetrievePdu(int type) {
+ if (mMessage == null) {
+ mMessage = new ByteArrayOutputStream();
+ mPosition = 0;
+ }
+
+ // X-Mms-Message-Type
+ appendOctet(PduHeaders.MESSAGE_TYPE);
+ appendOctet(type);
+
+ // X-Mms-Transaction-ID
+ appendOctet(PduHeaders.TRANSACTION_ID);
+
+ byte[] trid = mPduHeader.getTextString(PduHeaders.TRANSACTION_ID);
+ if (trid == null) {
+ // Transaction-ID should be set(by Transaction) before make().
+ throw new IllegalArgumentException("Transaction-ID is null.");
+ }
+ appendTextString(trid);
+
+ // X-Mms-MMS-Version
+ if (appendHeader(PduHeaders.MMS_VERSION) != PDU_COMPOSE_SUCCESS) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ // Date Date-value Optional.
+ appendHeader(PduHeaders.DATE);
+
+ // From
+ if (appendHeader(PduHeaders.FROM) != PDU_COMPOSE_SUCCESS) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ boolean recipient = false;
+
+ // To
+ if (appendHeader(PduHeaders.TO) != PDU_COMPOSE_CONTENT_ERROR) {
+ recipient = true;
+ }
+
+ // Cc
+ if (appendHeader(PduHeaders.CC) != PDU_COMPOSE_CONTENT_ERROR) {
+ recipient = true;
+ }
+
+ // Bcc
+ if (appendHeader(PduHeaders.BCC) != PDU_COMPOSE_CONTENT_ERROR) {
+ recipient = true;
+ }
+
+ // Need at least one of "cc", "bcc" and "to".
+ if (false == recipient) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ // Subject Optional
+ appendHeader(PduHeaders.SUBJECT);
+
+ // X-Mms-Message-Class Optional
+ // Message-class-value = Class-identifier | Token-text
+ appendHeader(PduHeaders.MESSAGE_CLASS);
+
+ // X-Mms-Expiry Optional
+ appendHeader(PduHeaders.EXPIRY);
+
+ // X-Mms-Priority Optional
+ appendHeader(PduHeaders.PRIORITY);
+
+ // X-Mms-Delivery-Report Optional
+ appendHeader(PduHeaders.DELIVERY_REPORT);
+
+ // X-Mms-Read-Report Optional
+ appendHeader(PduHeaders.READ_REPORT);
+
+ if (type == PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF) {
+ // X-Mms-Retrieve-Status Optional
+ appendHeader(PduHeaders.RETRIEVE_STATUS);
+ // X-Mms-Retrieve-Text Optional
+ appendHeader(PduHeaders.RETRIEVE_TEXT);
+ }
+
+
+ // Content-Type
+ appendOctet(PduHeaders.CONTENT_TYPE);
+
+ // Message body
+ return makeMessageBody(type);
+ }
+
+ /**
+ * Make message body.
+ */
+ private int makeMessageBody(int type) {
+ // 1. add body informations
+ mStack.newbuf(); // Switching buffer because we need to
+
+ PositionMarker ctStart = mStack.mark();
+
+ // This contentTypeIdentifier should be used for type of attachment...
+ String contentType = new String(mPduHeader.getTextString(PduHeaders.CONTENT_TYPE));
+ Integer contentTypeIdentifier = mContentTypeMap.get(contentType);
+ if (contentTypeIdentifier == null) {
+ // content type is mandatory
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ appendShortInteger(contentTypeIdentifier.intValue());
+
+ // content-type parameter: start
+ PduBody body;
+ if (type == PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF) {
+ body = ((RetrieveConf) mPdu).getBody();
+ } else {
+ body = ((SendReq) mPdu).getBody();
+ }
+ if (null == body || body.getPartsNum() == 0) {
+ // empty message
+ appendUintvarInteger(0);
+ mStack.pop();
+ mStack.copy();
+ return PDU_COMPOSE_SUCCESS;
+ }
+
+ PduPart part;
+ try {
+ part = body.getPart(0);
+
+ byte[] start = part.getContentId();
+ if (start != null) {
+ appendOctet(PduPart.P_DEP_START);
+ if (('<' == start[0]) && ('>' == start[start.length - 1])) {
+ appendTextString(start);
+ } else {
+ appendTextString("<" + new String(start) + ">");
+ }
+ }
+
+ // content-type parameter: type
+ appendOctet(PduPart.P_CT_MR_TYPE);
+ appendTextString(part.getContentType());
+ }
+ catch (ArrayIndexOutOfBoundsException e){
+ e.printStackTrace();
+ }
+
+ int ctLength = ctStart.getLength();
+ mStack.pop();
+ appendValueLength(ctLength);
+ mStack.copy();
+
+ // 3. add content
+ int partNum = body.getPartsNum();
+ appendUintvarInteger(partNum);
+ for (int i = 0; i < partNum; i++) {
+ part = body.getPart(i);
+ mStack.newbuf(); // Leaving space for header lengh and data length
+ PositionMarker attachment = mStack.mark();
+
+ mStack.newbuf(); // Leaving space for Content-Type length
+ PositionMarker contentTypeBegin = mStack.mark();
+
+ byte[] partContentType = part.getContentType();
+
+ if (partContentType == null) {
+ // content type is mandatory
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ // content-type value
+ Integer partContentTypeIdentifier =
+ mContentTypeMap.get(new String(partContentType));
+ if (partContentTypeIdentifier == null) {
+ appendTextString(partContentType);
+ } else {
+ appendShortInteger(partContentTypeIdentifier.intValue());
+ }
+
+ /* Content-type parameter : name.
+ * The value of name, filename, content-location is the same.
+ * Just one of them is enough for this PDU.
+ */
+ byte[] name = part.getName();
+
+ if (null == name) {
+ name = part.getFilename();
+
+ if (null == name) {
+ name = part.getContentLocation();
+
+ if (null == name) {
+ /* at lease one of name, filename, Content-location
+ * should be available.
+ */
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+ }
+ }
+ appendOctet(PduPart.P_DEP_NAME);
+ appendTextString(name);
+
+ // content-type parameter : charset
+ int charset = part.getCharset();
+ if (charset != 0) {
+ appendOctet(PduPart.P_CHARSET);
+ appendShortInteger(charset);
+ }
+
+ int contentTypeLength = contentTypeBegin.getLength();
+ mStack.pop();
+ appendValueLength(contentTypeLength);
+ mStack.copy();
+
+ // content id
+ byte[] contentId = part.getContentId();
+
+ if (null != contentId) {
+ appendOctet(PduPart.P_CONTENT_ID);
+ if (('<' == contentId[0]) && ('>' == contentId[contentId.length - 1])) {
+ appendQuotedString(contentId);
+ } else {
+ appendQuotedString("<" + new String(contentId) + ">");
+ }
+ }
+
+ // content-location
+ byte[] contentLocation = part.getContentLocation();
+ if (null != contentLocation) {
+ appendOctet(PduPart.P_CONTENT_LOCATION);
+ appendTextString(contentLocation);
+ }
+
+ // content
+ int headerLength = attachment.getLength();
+
+ int dataLength = 0; // Just for safety...
+ byte[] partData = part.getData();
+
+ if (partData != null) {
+ arraycopy(partData, 0, partData.length);
+ dataLength = partData.length;
+ } else {
+ InputStream cr = null;
+ try {
+ byte[] buffer = new byte[PDU_COMPOSER_BLOCK_SIZE];
+ cr = mResolver.openInputStream(part.getDataUri());
+ int len = 0;
+ while ((len = cr.read(buffer)) != -1) {
+ mMessage.write(buffer, 0, len);
+ mPosition += len;
+ dataLength += len;
+ }
+ } catch (FileNotFoundException e) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ } catch (IOException e) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ } catch (RuntimeException e) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ } finally {
+ if (cr != null) {
+ try {
+ cr.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+
+ if (dataLength != (attachment.getLength() - headerLength)) {
+ throw new RuntimeException("BUG: Length sanity check failed");
+ }
+
+ mStack.pop();
+ appendUintvarInteger(headerLength);
+ appendUintvarInteger(dataLength);
+ mStack.copy();
+ }
+
+ return PDU_COMPOSE_SUCCESS;
+ }
+
+ /**
+ * Record current message informations.
+ */
+ static private class LengthRecordNode {
+ ByteArrayOutputStream currentMessage = null;
+ public int currentPosition = 0;
+
+ public LengthRecordNode next = null;
+ }
+
+ /**
+ * Mark current message position and stact size.
+ */
+ private class PositionMarker {
+ private int c_pos; // Current position
+ private int currentStackSize; // Current stack size
+
+ @UnsupportedAppUsage
+ int getLength() {
+ // If these assert fails, likely that you are finding the
+ // size of buffer that is deep in BufferStack you can only
+ // find the length of the buffer that is on top
+ if (currentStackSize != mStack.stackSize) {
+ throw new RuntimeException("BUG: Invalid call to getLength()");
+ }
+
+ return mPosition - c_pos;
+ }
+ }
+
+ /**
+ * This implementation can be OPTIMIZED to use only
+ * 2 buffers. This optimization involves changing BufferStack
+ * only... Its usage (interface) will not change.
+ */
+ private class BufferStack {
+ private LengthRecordNode stack = null;
+ private LengthRecordNode toCopy = null;
+
+ int stackSize = 0;
+
+ /**
+ * Create a new message buffer and push it into the stack.
+ */
+ @UnsupportedAppUsage
+ void newbuf() {
+ // You can't create a new buff when toCopy != null
+ // That is after calling pop() and before calling copy()
+ // If you do, it is a bug
+ if (toCopy != null) {
+ throw new RuntimeException("BUG: Invalid newbuf() before copy()");
+ }
+
+ LengthRecordNode temp = new LengthRecordNode();
+
+ temp.currentMessage = mMessage;
+ temp.currentPosition = mPosition;
+
+ temp.next = stack;
+ stack = temp;
+
+ stackSize = stackSize + 1;
+
+ mMessage = new ByteArrayOutputStream();
+ mPosition = 0;
+ }
+
+ /**
+ * Pop the message before and record current message in the stack.
+ */
+ @UnsupportedAppUsage
+ void pop() {
+ ByteArrayOutputStream currentMessage = mMessage;
+ int currentPosition = mPosition;
+
+ mMessage = stack.currentMessage;
+ mPosition = stack.currentPosition;
+
+ toCopy = stack;
+ // Re using the top element of the stack to avoid memory allocation
+
+ stack = stack.next;
+ stackSize = stackSize - 1;
+
+ toCopy.currentMessage = currentMessage;
+ toCopy.currentPosition = currentPosition;
+ }
+
+ /**
+ * Append current message to the message before.
+ */
+ @UnsupportedAppUsage
+ void copy() {
+ arraycopy(toCopy.currentMessage.toByteArray(), 0,
+ toCopy.currentPosition);
+
+ toCopy = null;
+ }
+
+ /**
+ * Mark current message position
+ */
+ @UnsupportedAppUsage
+ PositionMarker mark() {
+ PositionMarker m = new PositionMarker();
+
+ m.c_pos = mPosition;
+ m.currentStackSize = stackSize;
+
+ return m;
+ }
+ }
+
+ /**
+ * Check address type.
+ *
+ * @param address address string without the postfix stinng type,
+ * such as "/TYPE=PLMN", "/TYPE=IPv6" and "/TYPE=IPv4"
+ * @return PDU_PHONE_NUMBER_ADDRESS_TYPE if it is phone number,
+ * PDU_EMAIL_ADDRESS_TYPE if it is email address,
+ * PDU_IPV4_ADDRESS_TYPE if it is ipv4 address,
+ * PDU_IPV6_ADDRESS_TYPE if it is ipv6 address,
+ * PDU_UNKNOWN_ADDRESS_TYPE if it is unknown.
+ */
+ protected static int checkAddressType(String address) {
+ /**
+ * From OMA-TS-MMS-ENC-V1_3-20050927-C.pdf, section 8.
+ * address = ( e-mail / device-address / alphanum-shortcode / num-shortcode)
+ * e-mail = mailbox; to the definition of mailbox as described in
+ * section 3.4 of [RFC2822], but excluding the
+ * obsolete definitions as indicated by the "obs-" prefix.
+ * device-address = ( global-phone-number "/TYPE=PLMN" )
+ * / ( ipv4 "/TYPE=IPv4" ) / ( ipv6 "/TYPE=IPv6" )
+ * / ( escaped-value "/TYPE=" address-type )
+ *
+ * global-phone-number = ["+"] 1*( DIGIT / written-sep )
+ * written-sep =("-"/".")
+ *
+ * ipv4 = 1*3DIGIT 3( "." 1*3DIGIT ) ; IPv4 address value
+ *
+ * ipv6 = 4HEXDIG 7( ":" 4HEXDIG ) ; IPv6 address per RFC 2373
+ */
+
+ if (null == address) {
+ return PDU_UNKNOWN_ADDRESS_TYPE;
+ }
+
+ if (address.matches(REGEXP_IPV4_ADDRESS_TYPE)) {
+ // Ipv4 address.
+ return PDU_IPV4_ADDRESS_TYPE;
+ }else if (address.matches(REGEXP_PHONE_NUMBER_ADDRESS_TYPE)) {
+ // Phone number.
+ return PDU_PHONE_NUMBER_ADDRESS_TYPE;
+ } else if (address.matches(REGEXP_EMAIL_ADDRESS_TYPE)) {
+ // Email address.
+ return PDU_EMAIL_ADDRESS_TYPE;
+ } else if (address.matches(REGEXP_IPV6_ADDRESS_TYPE)) {
+ // Ipv6 address.
+ return PDU_IPV6_ADDRESS_TYPE;
+ } else {
+ // Unknown address.
+ return PDU_UNKNOWN_ADDRESS_TYPE;
+ }
+ }
+}
diff --git a/telephony/java/com/google/android/mms/pdu/PduContentTypes.java b/telephony/java/com/google/android/mms/pdu/PduContentTypes.java
new file mode 100644
index 000000000000..8551b2f9b693
--- /dev/null
+++ b/telephony/java/com/google/android/mms/pdu/PduContentTypes.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+public class PduContentTypes {
+ /**
+ * All content types. From:
+ * http://www.openmobilealliance.org/tech/omna/omna-wsp-content-type.htm
+ */
+ @UnsupportedAppUsage
+ static final String[] contentTypes = {
+ "*/*", /* 0x00 */
+ "text/*", /* 0x01 */
+ "text/html", /* 0x02 */
+ "text/plain", /* 0x03 */
+ "text/x-hdml", /* 0x04 */
+ "text/x-ttml", /* 0x05 */
+ "text/x-vCalendar", /* 0x06 */
+ "text/x-vCard", /* 0x07 */
+ "text/vnd.wap.wml", /* 0x08 */
+ "text/vnd.wap.wmlscript", /* 0x09 */
+ "text/vnd.wap.wta-event", /* 0x0A */
+ "multipart/*", /* 0x0B */
+ "multipart/mixed", /* 0x0C */
+ "multipart/form-data", /* 0x0D */
+ "multipart/byterantes", /* 0x0E */
+ "multipart/alternative", /* 0x0F */
+ "application/*", /* 0x10 */
+ "application/java-vm", /* 0x11 */
+ "application/x-www-form-urlencoded", /* 0x12 */
+ "application/x-hdmlc", /* 0x13 */
+ "application/vnd.wap.wmlc", /* 0x14 */
+ "application/vnd.wap.wmlscriptc", /* 0x15 */
+ "application/vnd.wap.wta-eventc", /* 0x16 */
+ "application/vnd.wap.uaprof", /* 0x17 */
+ "application/vnd.wap.wtls-ca-certificate", /* 0x18 */
+ "application/vnd.wap.wtls-user-certificate", /* 0x19 */
+ "application/x-x509-ca-cert", /* 0x1A */
+ "application/x-x509-user-cert", /* 0x1B */
+ "image/*", /* 0x1C */
+ "image/gif", /* 0x1D */
+ "image/jpeg", /* 0x1E */
+ "image/tiff", /* 0x1F */
+ "image/png", /* 0x20 */
+ "image/vnd.wap.wbmp", /* 0x21 */
+ "application/vnd.wap.multipart.*", /* 0x22 */
+ "application/vnd.wap.multipart.mixed", /* 0x23 */
+ "application/vnd.wap.multipart.form-data", /* 0x24 */
+ "application/vnd.wap.multipart.byteranges", /* 0x25 */
+ "application/vnd.wap.multipart.alternative", /* 0x26 */
+ "application/xml", /* 0x27 */
+ "text/xml", /* 0x28 */
+ "application/vnd.wap.wbxml", /* 0x29 */
+ "application/x-x968-cross-cert", /* 0x2A */
+ "application/x-x968-ca-cert", /* 0x2B */
+ "application/x-x968-user-cert", /* 0x2C */
+ "text/vnd.wap.si", /* 0x2D */
+ "application/vnd.wap.sic", /* 0x2E */
+ "text/vnd.wap.sl", /* 0x2F */
+ "application/vnd.wap.slc", /* 0x30 */
+ "text/vnd.wap.co", /* 0x31 */
+ "application/vnd.wap.coc", /* 0x32 */
+ "application/vnd.wap.multipart.related", /* 0x33 */
+ "application/vnd.wap.sia", /* 0x34 */
+ "text/vnd.wap.connectivity-xml", /* 0x35 */
+ "application/vnd.wap.connectivity-wbxml", /* 0x36 */
+ "application/pkcs7-mime", /* 0x37 */
+ "application/vnd.wap.hashed-certificate", /* 0x38 */
+ "application/vnd.wap.signed-certificate", /* 0x39 */
+ "application/vnd.wap.cert-response", /* 0x3A */
+ "application/xhtml+xml", /* 0x3B */
+ "application/wml+xml", /* 0x3C */
+ "text/css", /* 0x3D */
+ "application/vnd.wap.mms-message", /* 0x3E */
+ "application/vnd.wap.rollover-certificate", /* 0x3F */
+ "application/vnd.wap.locc+wbxml", /* 0x40 */
+ "application/vnd.wap.loc+xml", /* 0x41 */
+ "application/vnd.syncml.dm+wbxml", /* 0x42 */
+ "application/vnd.syncml.dm+xml", /* 0x43 */
+ "application/vnd.syncml.notification", /* 0x44 */
+ "application/vnd.wap.xhtml+xml", /* 0x45 */
+ "application/vnd.wv.csp.cir", /* 0x46 */
+ "application/vnd.oma.dd+xml", /* 0x47 */
+ "application/vnd.oma.drm.message", /* 0x48 */
+ "application/vnd.oma.drm.content", /* 0x49 */
+ "application/vnd.oma.drm.rights+xml", /* 0x4A */
+ "application/vnd.oma.drm.rights+wbxml", /* 0x4B */
+ "application/vnd.wv.csp+xml", /* 0x4C */
+ "application/vnd.wv.csp+wbxml", /* 0x4D */
+ "application/vnd.syncml.ds.notification", /* 0x4E */
+ "audio/*", /* 0x4F */
+ "video/*", /* 0x50 */
+ "application/vnd.oma.dd2+xml", /* 0x51 */
+ "application/mikey" /* 0x52 */
+ };
+}
diff --git a/telephony/java/com/google/android/mms/pdu/PduHeaders.java b/telephony/java/com/google/android/mms/pdu/PduHeaders.java
new file mode 100644
index 000000000000..b5244645fda1
--- /dev/null
+++ b/telephony/java/com/google/android/mms/pdu/PduHeaders.java
@@ -0,0 +1,733 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import com.google.android.mms.InvalidHeaderValueException;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+public class PduHeaders {
+ /**
+ * All pdu header fields.
+ */
+ public static final int BCC = 0x81;
+ public static final int CC = 0x82;
+ public static final int CONTENT_LOCATION = 0x83;
+ public static final int CONTENT_TYPE = 0x84;
+ public static final int DATE = 0x85;
+ public static final int DELIVERY_REPORT = 0x86;
+ public static final int DELIVERY_TIME = 0x87;
+ public static final int EXPIRY = 0x88;
+ public static final int FROM = 0x89;
+ public static final int MESSAGE_CLASS = 0x8A;
+ public static final int MESSAGE_ID = 0x8B;
+ public static final int MESSAGE_TYPE = 0x8C;
+ public static final int MMS_VERSION = 0x8D;
+ public static final int MESSAGE_SIZE = 0x8E;
+ public static final int PRIORITY = 0x8F;
+
+ public static final int READ_REPLY = 0x90;
+ public static final int READ_REPORT = 0x90;
+ public static final int REPORT_ALLOWED = 0x91;
+ public static final int RESPONSE_STATUS = 0x92;
+ public static final int RESPONSE_TEXT = 0x93;
+ public static final int SENDER_VISIBILITY = 0x94;
+ public static final int STATUS = 0x95;
+ public static final int SUBJECT = 0x96;
+ public static final int TO = 0x97;
+ public static final int TRANSACTION_ID = 0x98;
+ public static final int RETRIEVE_STATUS = 0x99;
+ public static final int RETRIEVE_TEXT = 0x9A;
+ public static final int READ_STATUS = 0x9B;
+ public static final int REPLY_CHARGING = 0x9C;
+ public static final int REPLY_CHARGING_DEADLINE = 0x9D;
+ public static final int REPLY_CHARGING_ID = 0x9E;
+ public static final int REPLY_CHARGING_SIZE = 0x9F;
+
+ public static final int PREVIOUSLY_SENT_BY = 0xA0;
+ public static final int PREVIOUSLY_SENT_DATE = 0xA1;
+ public static final int STORE = 0xA2;
+ public static final int MM_STATE = 0xA3;
+ public static final int MM_FLAGS = 0xA4;
+ public static final int STORE_STATUS = 0xA5;
+ public static final int STORE_STATUS_TEXT = 0xA6;
+ public static final int STORED = 0xA7;
+ public static final int ATTRIBUTES = 0xA8;
+ public static final int TOTALS = 0xA9;
+ public static final int MBOX_TOTALS = 0xAA;
+ public static final int QUOTAS = 0xAB;
+ public static final int MBOX_QUOTAS = 0xAC;
+ public static final int MESSAGE_COUNT = 0xAD;
+ public static final int CONTENT = 0xAE;
+ public static final int START = 0xAF;
+
+ public static final int ADDITIONAL_HEADERS = 0xB0;
+ public static final int DISTRIBUTION_INDICATOR = 0xB1;
+ public static final int ELEMENT_DESCRIPTOR = 0xB2;
+ public static final int LIMIT = 0xB3;
+ public static final int RECOMMENDED_RETRIEVAL_MODE = 0xB4;
+ public static final int RECOMMENDED_RETRIEVAL_MODE_TEXT = 0xB5;
+ public static final int STATUS_TEXT = 0xB6;
+ public static final int APPLIC_ID = 0xB7;
+ public static final int REPLY_APPLIC_ID = 0xB8;
+ public static final int AUX_APPLIC_ID = 0xB9;
+ public static final int CONTENT_CLASS = 0xBA;
+ public static final int DRM_CONTENT = 0xBB;
+ public static final int ADAPTATION_ALLOWED = 0xBC;
+ public static final int REPLACE_ID = 0xBD;
+ public static final int CANCEL_ID = 0xBE;
+ public static final int CANCEL_STATUS = 0xBF;
+
+ /**
+ * X-Mms-Message-Type field types.
+ */
+ public static final int MESSAGE_TYPE_SEND_REQ = 0x80;
+ public static final int MESSAGE_TYPE_SEND_CONF = 0x81;
+ public static final int MESSAGE_TYPE_NOTIFICATION_IND = 0x82;
+ public static final int MESSAGE_TYPE_NOTIFYRESP_IND = 0x83;
+ public static final int MESSAGE_TYPE_RETRIEVE_CONF = 0x84;
+ public static final int MESSAGE_TYPE_ACKNOWLEDGE_IND = 0x85;
+ public static final int MESSAGE_TYPE_DELIVERY_IND = 0x86;
+ public static final int MESSAGE_TYPE_READ_REC_IND = 0x87;
+ public static final int MESSAGE_TYPE_READ_ORIG_IND = 0x88;
+ public static final int MESSAGE_TYPE_FORWARD_REQ = 0x89;
+ public static final int MESSAGE_TYPE_FORWARD_CONF = 0x8A;
+ public static final int MESSAGE_TYPE_MBOX_STORE_REQ = 0x8B;
+ public static final int MESSAGE_TYPE_MBOX_STORE_CONF = 0x8C;
+ public static final int MESSAGE_TYPE_MBOX_VIEW_REQ = 0x8D;
+ public static final int MESSAGE_TYPE_MBOX_VIEW_CONF = 0x8E;
+ public static final int MESSAGE_TYPE_MBOX_UPLOAD_REQ = 0x8F;
+ public static final int MESSAGE_TYPE_MBOX_UPLOAD_CONF = 0x90;
+ public static final int MESSAGE_TYPE_MBOX_DELETE_REQ = 0x91;
+ public static final int MESSAGE_TYPE_MBOX_DELETE_CONF = 0x92;
+ public static final int MESSAGE_TYPE_MBOX_DESCR = 0x93;
+ public static final int MESSAGE_TYPE_DELETE_REQ = 0x94;
+ public static final int MESSAGE_TYPE_DELETE_CONF = 0x95;
+ public static final int MESSAGE_TYPE_CANCEL_REQ = 0x96;
+ public static final int MESSAGE_TYPE_CANCEL_CONF = 0x97;
+
+ /**
+ * X-Mms-Delivery-Report |
+ * X-Mms-Read-Report |
+ * X-Mms-Report-Allowed |
+ * X-Mms-Sender-Visibility |
+ * X-Mms-Store |
+ * X-Mms-Stored |
+ * X-Mms-Totals |
+ * X-Mms-Quotas |
+ * X-Mms-Distribution-Indicator |
+ * X-Mms-DRM-Content |
+ * X-Mms-Adaptation-Allowed |
+ * field types.
+ */
+ public static final int VALUE_YES = 0x80;
+ public static final int VALUE_NO = 0x81;
+
+ /**
+ * Delivery-Time |
+ * Expiry and Reply-Charging-Deadline |
+ * field type components.
+ */
+ public static final int VALUE_ABSOLUTE_TOKEN = 0x80;
+ public static final int VALUE_RELATIVE_TOKEN = 0x81;
+
+ /**
+ * X-Mms-MMS-Version field types.
+ */
+ public static final int MMS_VERSION_1_3 = ((1 << 4) | 3);
+ public static final int MMS_VERSION_1_2 = ((1 << 4) | 2);
+ public static final int MMS_VERSION_1_1 = ((1 << 4) | 1);
+ public static final int MMS_VERSION_1_0 = ((1 << 4) | 0);
+
+ // Current version is 1.2.
+ public static final int CURRENT_MMS_VERSION = MMS_VERSION_1_2;
+
+ /**
+ * From field type components.
+ */
+ public static final int FROM_ADDRESS_PRESENT_TOKEN = 0x80;
+ public static final int FROM_INSERT_ADDRESS_TOKEN = 0x81;
+
+ public static final String FROM_ADDRESS_PRESENT_TOKEN_STR = "address-present-token";
+ public static final String FROM_INSERT_ADDRESS_TOKEN_STR = "insert-address-token";
+
+ /**
+ * X-Mms-Status Field.
+ */
+ public static final int STATUS_EXPIRED = 0x80;
+ public static final int STATUS_RETRIEVED = 0x81;
+ public static final int STATUS_REJECTED = 0x82;
+ public static final int STATUS_DEFERRED = 0x83;
+ public static final int STATUS_UNRECOGNIZED = 0x84;
+ public static final int STATUS_INDETERMINATE = 0x85;
+ public static final int STATUS_FORWARDED = 0x86;
+ public static final int STATUS_UNREACHABLE = 0x87;
+
+ /**
+ * MM-Flags field type components.
+ */
+ public static final int MM_FLAGS_ADD_TOKEN = 0x80;
+ public static final int MM_FLAGS_REMOVE_TOKEN = 0x81;
+ public static final int MM_FLAGS_FILTER_TOKEN = 0x82;
+
+ /**
+ * X-Mms-Message-Class field types.
+ */
+ public static final int MESSAGE_CLASS_PERSONAL = 0x80;
+ public static final int MESSAGE_CLASS_ADVERTISEMENT = 0x81;
+ public static final int MESSAGE_CLASS_INFORMATIONAL = 0x82;
+ public static final int MESSAGE_CLASS_AUTO = 0x83;
+
+ public static final String MESSAGE_CLASS_PERSONAL_STR = "personal";
+ public static final String MESSAGE_CLASS_ADVERTISEMENT_STR = "advertisement";
+ public static final String MESSAGE_CLASS_INFORMATIONAL_STR = "informational";
+ public static final String MESSAGE_CLASS_AUTO_STR = "auto";
+
+ /**
+ * X-Mms-Priority field types.
+ */
+ public static final int PRIORITY_LOW = 0x80;
+ public static final int PRIORITY_NORMAL = 0x81;
+ public static final int PRIORITY_HIGH = 0x82;
+
+ /**
+ * X-Mms-Response-Status field types.
+ */
+ public static final int RESPONSE_STATUS_OK = 0x80;
+ public static final int RESPONSE_STATUS_ERROR_UNSPECIFIED = 0x81;
+ public static final int RESPONSE_STATUS_ERROR_SERVICE_DENIED = 0x82;
+
+ public static final int RESPONSE_STATUS_ERROR_MESSAGE_FORMAT_CORRUPT = 0x83;
+ public static final int RESPONSE_STATUS_ERROR_SENDING_ADDRESS_UNRESOLVED = 0x84;
+
+ public static final int RESPONSE_STATUS_ERROR_MESSAGE_NOT_FOUND = 0x85;
+ public static final int RESPONSE_STATUS_ERROR_NETWORK_PROBLEM = 0x86;
+ public static final int RESPONSE_STATUS_ERROR_CONTENT_NOT_ACCEPTED = 0x87;
+ public static final int RESPONSE_STATUS_ERROR_UNSUPPORTED_MESSAGE = 0x88;
+ public static final int RESPONSE_STATUS_ERROR_TRANSIENT_FAILURE = 0xC0;
+
+ public static final int RESPONSE_STATUS_ERROR_TRANSIENT_SENDNG_ADDRESS_UNRESOLVED = 0xC1;
+ public static final int RESPONSE_STATUS_ERROR_TRANSIENT_MESSAGE_NOT_FOUND = 0xC2;
+ public static final int RESPONSE_STATUS_ERROR_TRANSIENT_NETWORK_PROBLEM = 0xC3;
+ public static final int RESPONSE_STATUS_ERROR_TRANSIENT_PARTIAL_SUCCESS = 0xC4;
+
+ public static final int RESPONSE_STATUS_ERROR_PERMANENT_FAILURE = 0xE0;
+ public static final int RESPONSE_STATUS_ERROR_PERMANENT_SERVICE_DENIED = 0xE1;
+ public static final int RESPONSE_STATUS_ERROR_PERMANENT_MESSAGE_FORMAT_CORRUPT = 0xE2;
+ public static final int RESPONSE_STATUS_ERROR_PERMANENT_SENDING_ADDRESS_UNRESOLVED = 0xE3;
+ public static final int RESPONSE_STATUS_ERROR_PERMANENT_MESSAGE_NOT_FOUND = 0xE4;
+ public static final int RESPONSE_STATUS_ERROR_PERMANENT_CONTENT_NOT_ACCEPTED = 0xE5;
+ public static final int RESPONSE_STATUS_ERROR_PERMANENT_REPLY_CHARGING_LIMITATIONS_NOT_MET = 0xE6;
+ public static final int RESPONSE_STATUS_ERROR_PERMANENT_REPLY_CHARGING_REQUEST_NOT_ACCEPTED = 0xE6;
+ public static final int RESPONSE_STATUS_ERROR_PERMANENT_REPLY_CHARGING_FORWARDING_DENIED = 0xE8;
+ public static final int RESPONSE_STATUS_ERROR_PERMANENT_REPLY_CHARGING_NOT_SUPPORTED = 0xE9;
+ public static final int RESPONSE_STATUS_ERROR_PERMANENT_ADDRESS_HIDING_NOT_SUPPORTED = 0xEA;
+ public static final int RESPONSE_STATUS_ERROR_PERMANENT_LACK_OF_PREPAID = 0xEB;
+ public static final int RESPONSE_STATUS_ERROR_PERMANENT_END = 0xFF;
+
+ /**
+ * X-Mms-Retrieve-Status field types.
+ */
+ public static final int RETRIEVE_STATUS_OK = 0x80;
+ public static final int RETRIEVE_STATUS_ERROR_TRANSIENT_FAILURE = 0xC0;
+ public static final int RETRIEVE_STATUS_ERROR_TRANSIENT_MESSAGE_NOT_FOUND = 0xC1;
+ public static final int RETRIEVE_STATUS_ERROR_TRANSIENT_NETWORK_PROBLEM = 0xC2;
+ public static final int RETRIEVE_STATUS_ERROR_PERMANENT_FAILURE = 0xE0;
+ public static final int RETRIEVE_STATUS_ERROR_PERMANENT_SERVICE_DENIED = 0xE1;
+ public static final int RETRIEVE_STATUS_ERROR_PERMANENT_MESSAGE_NOT_FOUND = 0xE2;
+ public static final int RETRIEVE_STATUS_ERROR_PERMANENT_CONTENT_UNSUPPORTED = 0xE3;
+ public static final int RETRIEVE_STATUS_ERROR_END = 0xFF;
+
+ /**
+ * X-Mms-Sender-Visibility field types.
+ */
+ public static final int SENDER_VISIBILITY_HIDE = 0x80;
+ public static final int SENDER_VISIBILITY_SHOW = 0x81;
+
+ /**
+ * X-Mms-Read-Status field types.
+ */
+ public static final int READ_STATUS_READ = 0x80;
+ public static final int READ_STATUS__DELETED_WITHOUT_BEING_READ = 0x81;
+
+ /**
+ * X-Mms-Cancel-Status field types.
+ */
+ public static final int CANCEL_STATUS_REQUEST_SUCCESSFULLY_RECEIVED = 0x80;
+ public static final int CANCEL_STATUS_REQUEST_CORRUPTED = 0x81;
+
+ /**
+ * X-Mms-Reply-Charging field types.
+ */
+ public static final int REPLY_CHARGING_REQUESTED = 0x80;
+ public static final int REPLY_CHARGING_REQUESTED_TEXT_ONLY = 0x81;
+ public static final int REPLY_CHARGING_ACCEPTED = 0x82;
+ public static final int REPLY_CHARGING_ACCEPTED_TEXT_ONLY = 0x83;
+
+ /**
+ * X-Mms-MM-State field types.
+ */
+ public static final int MM_STATE_DRAFT = 0x80;
+ public static final int MM_STATE_SENT = 0x81;
+ public static final int MM_STATE_NEW = 0x82;
+ public static final int MM_STATE_RETRIEVED = 0x83;
+ public static final int MM_STATE_FORWARDED = 0x84;
+
+ /**
+ * X-Mms-Recommended-Retrieval-Mode field types.
+ */
+ public static final int RECOMMENDED_RETRIEVAL_MODE_MANUAL = 0x80;
+
+ /**
+ * X-Mms-Content-Class field types.
+ */
+ public static final int CONTENT_CLASS_TEXT = 0x80;
+ public static final int CONTENT_CLASS_IMAGE_BASIC = 0x81;
+ public static final int CONTENT_CLASS_IMAGE_RICH = 0x82;
+ public static final int CONTENT_CLASS_VIDEO_BASIC = 0x83;
+ public static final int CONTENT_CLASS_VIDEO_RICH = 0x84;
+ public static final int CONTENT_CLASS_MEGAPIXEL = 0x85;
+ public static final int CONTENT_CLASS_CONTENT_BASIC = 0x86;
+ public static final int CONTENT_CLASS_CONTENT_RICH = 0x87;
+
+ /**
+ * X-Mms-Store-Status field types.
+ */
+ public static final int STORE_STATUS_SUCCESS = 0x80;
+ public static final int STORE_STATUS_ERROR_TRANSIENT_FAILURE = 0xC0;
+ public static final int STORE_STATUS_ERROR_TRANSIENT_NETWORK_PROBLEM = 0xC1;
+ public static final int STORE_STATUS_ERROR_PERMANENT_FAILURE = 0xE0;
+ public static final int STORE_STATUS_ERROR_PERMANENT_SERVICE_DENIED = 0xE1;
+ public static final int STORE_STATUS_ERROR_PERMANENT_MESSAGE_FORMAT_CORRUPT = 0xE2;
+ public static final int STORE_STATUS_ERROR_PERMANENT_MESSAGE_NOT_FOUND = 0xE3;
+ public static final int STORE_STATUS_ERROR_PERMANENT_MMBOX_FULL = 0xE4;
+ public static final int STORE_STATUS_ERROR_END = 0xFF;
+
+ /**
+ * The map contains the value of all headers.
+ */
+ private HashMap<Integer, Object> mHeaderMap = null;
+
+ /**
+ * Constructor of PduHeaders.
+ */
+ @UnsupportedAppUsage
+ public PduHeaders() {
+ mHeaderMap = new HashMap<Integer, Object>();
+ }
+
+ /**
+ * Get octet value by header field.
+ *
+ * @param field the field
+ * @return the octet value of the pdu header
+ * with specified header field. Return 0 if
+ * the value is not set.
+ */
+ @UnsupportedAppUsage
+ protected int getOctet(int field) {
+ Integer octet = (Integer) mHeaderMap.get(field);
+ if (null == octet) {
+ return 0;
+ }
+
+ return octet;
+ }
+
+ /**
+ * Set octet value to pdu header by header field.
+ *
+ * @param value the value
+ * @param field the field
+ * @throws InvalidHeaderValueException if the value is invalid.
+ */
+ @UnsupportedAppUsage
+ protected void setOctet(int value, int field)
+ throws InvalidHeaderValueException{
+ /**
+ * Check whether this field can be set for specific
+ * header and check validity of the field.
+ */
+ switch (field) {
+ case REPORT_ALLOWED:
+ case ADAPTATION_ALLOWED:
+ case DELIVERY_REPORT:
+ case DRM_CONTENT:
+ case DISTRIBUTION_INDICATOR:
+ case QUOTAS:
+ case READ_REPORT:
+ case STORE:
+ case STORED:
+ case TOTALS:
+ case SENDER_VISIBILITY:
+ if ((VALUE_YES != value) && (VALUE_NO != value)) {
+ // Invalid value.
+ throw new InvalidHeaderValueException("Invalid Octet value!");
+ }
+ break;
+ case READ_STATUS:
+ if ((READ_STATUS_READ != value) &&
+ (READ_STATUS__DELETED_WITHOUT_BEING_READ != value)) {
+ // Invalid value.
+ throw new InvalidHeaderValueException("Invalid Octet value!");
+ }
+ break;
+ case CANCEL_STATUS:
+ if ((CANCEL_STATUS_REQUEST_SUCCESSFULLY_RECEIVED != value) &&
+ (CANCEL_STATUS_REQUEST_CORRUPTED != value)) {
+ // Invalid value.
+ throw new InvalidHeaderValueException("Invalid Octet value!");
+ }
+ break;
+ case PRIORITY:
+ if ((value < PRIORITY_LOW) || (value > PRIORITY_HIGH)) {
+ // Invalid value.
+ throw new InvalidHeaderValueException("Invalid Octet value!");
+ }
+ break;
+ case STATUS:
+ if ((value < STATUS_EXPIRED) || (value > STATUS_UNREACHABLE)) {
+ // Invalid value.
+ throw new InvalidHeaderValueException("Invalid Octet value!");
+ }
+ break;
+ case REPLY_CHARGING:
+ if ((value < REPLY_CHARGING_REQUESTED)
+ || (value > REPLY_CHARGING_ACCEPTED_TEXT_ONLY)) {
+ // Invalid value.
+ throw new InvalidHeaderValueException("Invalid Octet value!");
+ }
+ break;
+ case MM_STATE:
+ if ((value < MM_STATE_DRAFT) || (value > MM_STATE_FORWARDED)) {
+ // Invalid value.
+ throw new InvalidHeaderValueException("Invalid Octet value!");
+ }
+ break;
+ case RECOMMENDED_RETRIEVAL_MODE:
+ if (RECOMMENDED_RETRIEVAL_MODE_MANUAL != value) {
+ // Invalid value.
+ throw new InvalidHeaderValueException("Invalid Octet value!");
+ }
+ break;
+ case CONTENT_CLASS:
+ if ((value < CONTENT_CLASS_TEXT)
+ || (value > CONTENT_CLASS_CONTENT_RICH)) {
+ // Invalid value.
+ throw new InvalidHeaderValueException("Invalid Octet value!");
+ }
+ break;
+ case RETRIEVE_STATUS:
+ // According to oma-ts-mms-enc-v1_3, section 7.3.50, we modify the invalid value.
+ if ((value > RETRIEVE_STATUS_ERROR_TRANSIENT_NETWORK_PROBLEM) &&
+ (value < RETRIEVE_STATUS_ERROR_PERMANENT_FAILURE)) {
+ value = RETRIEVE_STATUS_ERROR_TRANSIENT_FAILURE;
+ } else if ((value > RETRIEVE_STATUS_ERROR_PERMANENT_CONTENT_UNSUPPORTED) &&
+ (value <= RETRIEVE_STATUS_ERROR_END)) {
+ value = RETRIEVE_STATUS_ERROR_PERMANENT_FAILURE;
+ } else if ((value < RETRIEVE_STATUS_OK) ||
+ ((value > RETRIEVE_STATUS_OK) &&
+ (value < RETRIEVE_STATUS_ERROR_TRANSIENT_FAILURE)) ||
+ (value > RETRIEVE_STATUS_ERROR_END)) {
+ value = RETRIEVE_STATUS_ERROR_PERMANENT_FAILURE;
+ }
+ break;
+ case STORE_STATUS:
+ // According to oma-ts-mms-enc-v1_3, section 7.3.58, we modify the invalid value.
+ if ((value > STORE_STATUS_ERROR_TRANSIENT_NETWORK_PROBLEM) &&
+ (value < STORE_STATUS_ERROR_PERMANENT_FAILURE)) {
+ value = STORE_STATUS_ERROR_TRANSIENT_FAILURE;
+ } else if ((value > STORE_STATUS_ERROR_PERMANENT_MMBOX_FULL) &&
+ (value <= STORE_STATUS_ERROR_END)) {
+ value = STORE_STATUS_ERROR_PERMANENT_FAILURE;
+ } else if ((value < STORE_STATUS_SUCCESS) ||
+ ((value > STORE_STATUS_SUCCESS) &&
+ (value < STORE_STATUS_ERROR_TRANSIENT_FAILURE)) ||
+ (value > STORE_STATUS_ERROR_END)) {
+ value = STORE_STATUS_ERROR_PERMANENT_FAILURE;
+ }
+ break;
+ case RESPONSE_STATUS:
+ // According to oma-ts-mms-enc-v1_3, section 7.3.48, we modify the invalid value.
+ if ((value > RESPONSE_STATUS_ERROR_TRANSIENT_PARTIAL_SUCCESS) &&
+ (value < RESPONSE_STATUS_ERROR_PERMANENT_FAILURE)) {
+ value = RESPONSE_STATUS_ERROR_TRANSIENT_FAILURE;
+ } else if (((value > RESPONSE_STATUS_ERROR_PERMANENT_LACK_OF_PREPAID) &&
+ (value <= RESPONSE_STATUS_ERROR_PERMANENT_END)) ||
+ (value < RESPONSE_STATUS_OK) ||
+ ((value > RESPONSE_STATUS_ERROR_UNSUPPORTED_MESSAGE) &&
+ (value < RESPONSE_STATUS_ERROR_TRANSIENT_FAILURE)) ||
+ (value > RESPONSE_STATUS_ERROR_PERMANENT_END)) {
+ value = RESPONSE_STATUS_ERROR_PERMANENT_FAILURE;
+ }
+ break;
+ case MMS_VERSION:
+ if ((value < MMS_VERSION_1_0)|| (value > MMS_VERSION_1_3)) {
+ value = CURRENT_MMS_VERSION; // Current version is the default value.
+ }
+ break;
+ case MESSAGE_TYPE:
+ if ((value < MESSAGE_TYPE_SEND_REQ) || (value > MESSAGE_TYPE_CANCEL_CONF)) {
+ // Invalid value.
+ throw new InvalidHeaderValueException("Invalid Octet value!");
+ }
+ break;
+ default:
+ // This header value should not be Octect.
+ throw new RuntimeException("Invalid header field!");
+ }
+ mHeaderMap.put(field, value);
+ }
+
+ /**
+ * Get TextString value by header field.
+ *
+ * @param field the field
+ * @return the TextString value of the pdu header
+ * with specified header field
+ */
+ @UnsupportedAppUsage
+ protected byte[] getTextString(int field) {
+ return (byte[]) mHeaderMap.get(field);
+ }
+
+ /**
+ * Set TextString value to pdu header by header field.
+ *
+ * @param value the value
+ * @param field the field
+ * @return the TextString value of the pdu header
+ * with specified header field
+ * @throws NullPointerException if the value is null.
+ */
+ protected void setTextString(byte[] value, int field) {
+ /**
+ * Check whether this field can be set for specific
+ * header and check validity of the field.
+ */
+ if (null == value) {
+ throw new NullPointerException();
+ }
+
+ switch (field) {
+ case TRANSACTION_ID:
+ case REPLY_CHARGING_ID:
+ case AUX_APPLIC_ID:
+ case APPLIC_ID:
+ case REPLY_APPLIC_ID:
+ case MESSAGE_ID:
+ case REPLACE_ID:
+ case CANCEL_ID:
+ case CONTENT_LOCATION:
+ case MESSAGE_CLASS:
+ case CONTENT_TYPE:
+ break;
+ default:
+ // This header value should not be Text-String.
+ throw new RuntimeException("Invalid header field!");
+ }
+ mHeaderMap.put(field, value);
+ }
+
+ /**
+ * Get EncodedStringValue value by header field.
+ *
+ * @param field the field
+ * @return the EncodedStringValue value of the pdu header
+ * with specified header field
+ */
+ @UnsupportedAppUsage
+ protected EncodedStringValue getEncodedStringValue(int field) {
+ return (EncodedStringValue) mHeaderMap.get(field);
+ }
+
+ /**
+ * Get TO, CC or BCC header value.
+ *
+ * @param field the field
+ * @return the EncodeStringValue array of the pdu header
+ * with specified header field
+ */
+ @UnsupportedAppUsage
+ protected EncodedStringValue[] getEncodedStringValues(int field) {
+ ArrayList<EncodedStringValue> list =
+ (ArrayList<EncodedStringValue>) mHeaderMap.get(field);
+ if (null == list) {
+ return null;
+ }
+ EncodedStringValue[] values = new EncodedStringValue[list.size()];
+ return list.toArray(values);
+ }
+
+ /**
+ * Set EncodedStringValue value to pdu header by header field.
+ *
+ * @param value the value
+ * @param field the field
+ * @return the EncodedStringValue value of the pdu header
+ * with specified header field
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ protected void setEncodedStringValue(EncodedStringValue value, int field) {
+ /**
+ * Check whether this field can be set for specific
+ * header and check validity of the field.
+ */
+ if (null == value) {
+ throw new NullPointerException();
+ }
+
+ switch (field) {
+ case SUBJECT:
+ case RECOMMENDED_RETRIEVAL_MODE_TEXT:
+ case RETRIEVE_TEXT:
+ case STATUS_TEXT:
+ case STORE_STATUS_TEXT:
+ case RESPONSE_TEXT:
+ case FROM:
+ case PREVIOUSLY_SENT_BY:
+ case MM_FLAGS:
+ break;
+ default:
+ // This header value should not be Encoded-String-Value.
+ throw new RuntimeException("Invalid header field!");
+ }
+
+ mHeaderMap.put(field, value);
+ }
+
+ /**
+ * Set TO, CC or BCC header value.
+ *
+ * @param value the value
+ * @param field the field
+ * @return the EncodedStringValue value array of the pdu header
+ * with specified header field
+ * @throws NullPointerException if the value is null.
+ */
+ protected void setEncodedStringValues(EncodedStringValue[] value, int field) {
+ /**
+ * Check whether this field can be set for specific
+ * header and check validity of the field.
+ */
+ if (null == value) {
+ throw new NullPointerException();
+ }
+
+ switch (field) {
+ case BCC:
+ case CC:
+ case TO:
+ break;
+ default:
+ // This header value should not be Encoded-String-Value.
+ throw new RuntimeException("Invalid header field!");
+ }
+
+ ArrayList<EncodedStringValue> list = new ArrayList<EncodedStringValue>();
+ for (int i = 0; i < value.length; i++) {
+ list.add(value[i]);
+ }
+ mHeaderMap.put(field, list);
+ }
+
+ /**
+ * Append one EncodedStringValue to another.
+ *
+ * @param value the EncodedStringValue to append
+ * @param field the field
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ protected void appendEncodedStringValue(EncodedStringValue value,
+ int field) {
+ if (null == value) {
+ throw new NullPointerException();
+ }
+
+ switch (field) {
+ case BCC:
+ case CC:
+ case TO:
+ break;
+ default:
+ throw new RuntimeException("Invalid header field!");
+ }
+
+ ArrayList<EncodedStringValue> list =
+ (ArrayList<EncodedStringValue>) mHeaderMap.get(field);
+ if (null == list) {
+ list = new ArrayList<EncodedStringValue>();
+ }
+ list.add(value);
+ mHeaderMap.put(field, list);
+ }
+
+ /**
+ * Get LongInteger value by header field.
+ *
+ * @param field the field
+ * @return the LongInteger value of the pdu header
+ * with specified header field. if return -1, the
+ * field is not existed in pdu header.
+ */
+ @UnsupportedAppUsage
+ protected long getLongInteger(int field) {
+ Long longInteger = (Long) mHeaderMap.get(field);
+ if (null == longInteger) {
+ return -1;
+ }
+
+ return longInteger.longValue();
+ }
+
+ /**
+ * Set LongInteger value to pdu header by header field.
+ *
+ * @param value the value
+ * @param field the field
+ */
+ @UnsupportedAppUsage
+ protected void setLongInteger(long value, int field) {
+ /**
+ * Check whether this field can be set for specific
+ * header and check validity of the field.
+ */
+ switch (field) {
+ case DATE:
+ case REPLY_CHARGING_SIZE:
+ case MESSAGE_SIZE:
+ case MESSAGE_COUNT:
+ case START:
+ case LIMIT:
+ case DELIVERY_TIME:
+ case EXPIRY:
+ case REPLY_CHARGING_DEADLINE:
+ case PREVIOUSLY_SENT_DATE:
+ break;
+ default:
+ // This header value should not be LongInteger.
+ throw new RuntimeException("Invalid header field!");
+ }
+ mHeaderMap.put(field, value);
+ }
+}
diff --git a/telephony/java/com/google/android/mms/pdu/PduParser.java b/telephony/java/com/google/android/mms/pdu/PduParser.java
new file mode 100755
index 000000000000..f48399410723
--- /dev/null
+++ b/telephony/java/com/google/android/mms/pdu/PduParser.java
@@ -0,0 +1,2023 @@
+/*
+ * Copyright (C) 2007-2008 Esmertec AG.
+ * Copyright (C) 2007-2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.pdu;
+
+import android.util.Log;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import com.google.android.mms.ContentType;
+import com.google.android.mms.InvalidHeaderValueException;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.Arrays;
+import java.util.HashMap;
+
+public class PduParser {
+ /**
+ * The next are WAP values defined in WSP specification.
+ */
+ private static final int QUOTE = 127;
+ private static final int LENGTH_QUOTE = 31;
+ private static final int TEXT_MIN = 32;
+ private static final int TEXT_MAX = 127;
+ private static final int SHORT_INTEGER_MAX = 127;
+ private static final int SHORT_LENGTH_MAX = 30;
+ private static final int LONG_INTEGER_LENGTH_MAX = 8;
+ private static final int QUOTED_STRING_FLAG = 34;
+ private static final int END_STRING_FLAG = 0x00;
+ //The next two are used by the interface "parseWapString" to
+ //distinguish Text-String and Quoted-String.
+ private static final int TYPE_TEXT_STRING = 0;
+ private static final int TYPE_QUOTED_STRING = 1;
+ private static final int TYPE_TOKEN_STRING = 2;
+
+ /**
+ * Specify the part position.
+ */
+ private static final int THE_FIRST_PART = 0;
+ private static final int THE_LAST_PART = 1;
+
+ /**
+ * The pdu data.
+ */
+ private ByteArrayInputStream mPduDataStream = null;
+
+ /**
+ * Store pdu headers
+ */
+ private PduHeaders mHeaders = null;
+
+ /**
+ * Store pdu parts.
+ */
+ private PduBody mBody = null;
+
+ /**
+ * Store the "type" parameter in "Content-Type" header field.
+ */
+ private static byte[] mTypeParam = null;
+
+ /**
+ * Store the "start" parameter in "Content-Type" header field.
+ */
+ private static byte[] mStartParam = null;
+
+ /**
+ * The log tag.
+ */
+ private static final String LOG_TAG = "PduParser";
+ private static final boolean DEBUG = false;
+ private static final boolean LOCAL_LOGV = false;
+
+ /**
+ * Whether to parse content-disposition part header
+ */
+ private final boolean mParseContentDisposition;
+
+ /**
+ * Constructor.
+ *
+ * @param pduDataStream pdu data to be parsed
+ * @param parseContentDisposition whether to parse the Content-Disposition part header
+ */
+ @UnsupportedAppUsage
+ public PduParser(byte[] pduDataStream, boolean parseContentDisposition) {
+ mPduDataStream = new ByteArrayInputStream(pduDataStream);
+ mParseContentDisposition = parseContentDisposition;
+ }
+
+ /**
+ * Parse the pdu.
+ *
+ * @return the pdu structure if parsing successfully.
+ * null if parsing error happened or mandatory fields are not set.
+ */
+ @UnsupportedAppUsage
+ public GenericPdu parse(){
+ if (mPduDataStream == null) {
+ return null;
+ }
+
+ /* parse headers */
+ mHeaders = parseHeaders(mPduDataStream);
+ if (null == mHeaders) {
+ // Parse headers failed.
+ return null;
+ }
+
+ /* get the message type */
+ int messageType = mHeaders.getOctet(PduHeaders.MESSAGE_TYPE);
+
+ /* check mandatory header fields */
+ if (false == checkMandatoryHeader(mHeaders)) {
+ log("check mandatory headers failed!");
+ return null;
+ }
+
+ if ((PduHeaders.MESSAGE_TYPE_SEND_REQ == messageType) ||
+ (PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF == messageType)) {
+ /* need to parse the parts */
+ mBody = parseParts(mPduDataStream);
+ if (null == mBody) {
+ // Parse parts failed.
+ return null;
+ }
+ }
+
+ switch (messageType) {
+ case PduHeaders.MESSAGE_TYPE_SEND_REQ:
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parse: MESSAGE_TYPE_SEND_REQ");
+ }
+ SendReq sendReq = new SendReq(mHeaders, mBody);
+ return sendReq;
+ case PduHeaders.MESSAGE_TYPE_SEND_CONF:
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parse: MESSAGE_TYPE_SEND_CONF");
+ }
+ SendConf sendConf = new SendConf(mHeaders);
+ return sendConf;
+ case PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND:
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parse: MESSAGE_TYPE_NOTIFICATION_IND");
+ }
+ NotificationInd notificationInd =
+ new NotificationInd(mHeaders);
+ return notificationInd;
+ case PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND:
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parse: MESSAGE_TYPE_NOTIFYRESP_IND");
+ }
+ NotifyRespInd notifyRespInd =
+ new NotifyRespInd(mHeaders);
+ return notifyRespInd;
+ case PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF:
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parse: MESSAGE_TYPE_RETRIEVE_CONF");
+ }
+ RetrieveConf retrieveConf =
+ new RetrieveConf(mHeaders, mBody);
+
+ byte[] contentType = retrieveConf.getContentType();
+ if (null == contentType) {
+ return null;
+ }
+ String ctTypeStr = new String(contentType);
+ if (ctTypeStr.equals(ContentType.MULTIPART_MIXED)
+ || ctTypeStr.equals(ContentType.MULTIPART_RELATED)
+ || ctTypeStr.equals(ContentType.MULTIPART_ALTERNATIVE)) {
+ // The MMS content type must be "application/vnd.wap.multipart.mixed"
+ // or "application/vnd.wap.multipart.related"
+ // or "application/vnd.wap.multipart.alternative"
+ return retrieveConf;
+ } else if (ctTypeStr.equals(ContentType.MULTIPART_ALTERNATIVE)) {
+ // "application/vnd.wap.multipart.alternative"
+ // should take only the first part.
+ PduPart firstPart = mBody.getPart(0);
+ mBody.removeAll();
+ mBody.addPart(0, firstPart);
+ return retrieveConf;
+ }
+ return null;
+ case PduHeaders.MESSAGE_TYPE_DELIVERY_IND:
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parse: MESSAGE_TYPE_DELIVERY_IND");
+ }
+ DeliveryInd deliveryInd =
+ new DeliveryInd(mHeaders);
+ return deliveryInd;
+ case PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND:
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parse: MESSAGE_TYPE_ACKNOWLEDGE_IND");
+ }
+ AcknowledgeInd acknowledgeInd =
+ new AcknowledgeInd(mHeaders);
+ return acknowledgeInd;
+ case PduHeaders.MESSAGE_TYPE_READ_ORIG_IND:
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parse: MESSAGE_TYPE_READ_ORIG_IND");
+ }
+ ReadOrigInd readOrigInd =
+ new ReadOrigInd(mHeaders);
+ return readOrigInd;
+ case PduHeaders.MESSAGE_TYPE_READ_REC_IND:
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parse: MESSAGE_TYPE_READ_REC_IND");
+ }
+ ReadRecInd readRecInd =
+ new ReadRecInd(mHeaders);
+ return readRecInd;
+ default:
+ log("Parser doesn't support this message type in this version!");
+ return null;
+ }
+ }
+
+ /**
+ * Parse pdu headers.
+ *
+ * @param pduDataStream pdu data input stream
+ * @return headers in PduHeaders structure, null when parse fail
+ */
+ protected PduHeaders parseHeaders(ByteArrayInputStream pduDataStream){
+ if (pduDataStream == null) {
+ return null;
+ }
+ boolean keepParsing = true;
+ PduHeaders headers = new PduHeaders();
+
+ while (keepParsing && (pduDataStream.available() > 0)) {
+ pduDataStream.mark(1);
+ int headerField = extractByteValue(pduDataStream);
+ /* parse custom text header */
+ if ((headerField >= TEXT_MIN) && (headerField <= TEXT_MAX)) {
+ pduDataStream.reset();
+ byte [] bVal = parseWapString(pduDataStream, TYPE_TEXT_STRING);
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "TextHeader: " + new String(bVal));
+ }
+ /* we should ignore it at the moment */
+ continue;
+ }
+ switch (headerField) {
+ case PduHeaders.MESSAGE_TYPE:
+ {
+ int messageType = extractByteValue(pduDataStream);
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: messageType: " + messageType);
+ }
+ switch (messageType) {
+ // We don't support these kind of messages now.
+ case PduHeaders.MESSAGE_TYPE_FORWARD_REQ:
+ case PduHeaders.MESSAGE_TYPE_FORWARD_CONF:
+ case PduHeaders.MESSAGE_TYPE_MBOX_STORE_REQ:
+ case PduHeaders.MESSAGE_TYPE_MBOX_STORE_CONF:
+ case PduHeaders.MESSAGE_TYPE_MBOX_VIEW_REQ:
+ case PduHeaders.MESSAGE_TYPE_MBOX_VIEW_CONF:
+ case PduHeaders.MESSAGE_TYPE_MBOX_UPLOAD_REQ:
+ case PduHeaders.MESSAGE_TYPE_MBOX_UPLOAD_CONF:
+ case PduHeaders.MESSAGE_TYPE_MBOX_DELETE_REQ:
+ case PduHeaders.MESSAGE_TYPE_MBOX_DELETE_CONF:
+ case PduHeaders.MESSAGE_TYPE_MBOX_DESCR:
+ case PduHeaders.MESSAGE_TYPE_DELETE_REQ:
+ case PduHeaders.MESSAGE_TYPE_DELETE_CONF:
+ case PduHeaders.MESSAGE_TYPE_CANCEL_REQ:
+ case PduHeaders.MESSAGE_TYPE_CANCEL_CONF:
+ return null;
+ }
+ try {
+ headers.setOctet(messageType, headerField);
+ } catch(InvalidHeaderValueException e) {
+ log("Set invalid Octet value: " + messageType +
+ " into the header filed: " + headerField);
+ return null;
+ } catch(RuntimeException e) {
+ log(headerField + "is not Octet header field!");
+ return null;
+ }
+ break;
+ }
+ /* Octect value */
+ case PduHeaders.REPORT_ALLOWED:
+ case PduHeaders.ADAPTATION_ALLOWED:
+ case PduHeaders.DELIVERY_REPORT:
+ case PduHeaders.DRM_CONTENT:
+ case PduHeaders.DISTRIBUTION_INDICATOR:
+ case PduHeaders.QUOTAS:
+ case PduHeaders.READ_REPORT:
+ case PduHeaders.STORE:
+ case PduHeaders.STORED:
+ case PduHeaders.TOTALS:
+ case PduHeaders.SENDER_VISIBILITY:
+ case PduHeaders.READ_STATUS:
+ case PduHeaders.CANCEL_STATUS:
+ case PduHeaders.PRIORITY:
+ case PduHeaders.STATUS:
+ case PduHeaders.REPLY_CHARGING:
+ case PduHeaders.MM_STATE:
+ case PduHeaders.RECOMMENDED_RETRIEVAL_MODE:
+ case PduHeaders.CONTENT_CLASS:
+ case PduHeaders.RETRIEVE_STATUS:
+ case PduHeaders.STORE_STATUS:
+ /**
+ * The following field has a different value when
+ * used in the M-Mbox-Delete.conf and M-Delete.conf PDU.
+ * For now we ignore this fact, since we do not support these PDUs
+ */
+ case PduHeaders.RESPONSE_STATUS:
+ {
+ int value = extractByteValue(pduDataStream);
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: byte: " + headerField + " value: " +
+ value);
+ }
+
+ try {
+ headers.setOctet(value, headerField);
+ } catch(InvalidHeaderValueException e) {
+ log("Set invalid Octet value: " + value +
+ " into the header filed: " + headerField);
+ return null;
+ } catch(RuntimeException e) {
+ log(headerField + "is not Octet header field!");
+ return null;
+ }
+ break;
+ }
+
+ /* Long-Integer */
+ case PduHeaders.DATE:
+ case PduHeaders.REPLY_CHARGING_SIZE:
+ case PduHeaders.MESSAGE_SIZE:
+ {
+ try {
+ long value = parseLongInteger(pduDataStream);
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: longint: " + headerField + " value: " +
+ value);
+ }
+ headers.setLongInteger(value, headerField);
+ } catch(RuntimeException e) {
+ log(headerField + "is not Long-Integer header field!");
+ return null;
+ }
+ break;
+ }
+
+ /* Integer-Value */
+ case PduHeaders.MESSAGE_COUNT:
+ case PduHeaders.START:
+ case PduHeaders.LIMIT:
+ {
+ try {
+ long value = parseIntegerValue(pduDataStream);
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: int: " + headerField + " value: " +
+ value);
+ }
+ headers.setLongInteger(value, headerField);
+ } catch(RuntimeException e) {
+ log(headerField + "is not Long-Integer header field!");
+ return null;
+ }
+ break;
+ }
+
+ /* Text-String */
+ case PduHeaders.TRANSACTION_ID:
+ case PduHeaders.REPLY_CHARGING_ID:
+ case PduHeaders.AUX_APPLIC_ID:
+ case PduHeaders.APPLIC_ID:
+ case PduHeaders.REPLY_APPLIC_ID:
+ /**
+ * The next three header fields are email addresses
+ * as defined in RFC2822,
+ * not including the characters "<" and ">"
+ */
+ case PduHeaders.MESSAGE_ID:
+ case PduHeaders.REPLACE_ID:
+ case PduHeaders.CANCEL_ID:
+ /**
+ * The following field has a different value when
+ * used in the M-Mbox-Delete.conf and M-Delete.conf PDU.
+ * For now we ignore this fact, since we do not support these PDUs
+ */
+ case PduHeaders.CONTENT_LOCATION:
+ {
+ byte[] value = parseWapString(pduDataStream, TYPE_TEXT_STRING);
+ if (null != value) {
+ try {
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: string: " + headerField + " value: " +
+ new String(value));
+ }
+ headers.setTextString(value, headerField);
+ } catch(NullPointerException e) {
+ log("null pointer error!");
+ } catch(RuntimeException e) {
+ log(headerField + "is not Text-String header field!");
+ return null;
+ }
+ }
+ break;
+ }
+
+ /* Encoded-string-value */
+ case PduHeaders.SUBJECT:
+ case PduHeaders.RECOMMENDED_RETRIEVAL_MODE_TEXT:
+ case PduHeaders.RETRIEVE_TEXT:
+ case PduHeaders.STATUS_TEXT:
+ case PduHeaders.STORE_STATUS_TEXT:
+ /* the next one is not support
+ * M-Mbox-Delete.conf and M-Delete.conf now */
+ case PduHeaders.RESPONSE_TEXT:
+ {
+ EncodedStringValue value =
+ parseEncodedStringValue(pduDataStream);
+ if (null != value) {
+ try {
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: encoded string: " + headerField
+ + " value: " + value.getString());
+ }
+ headers.setEncodedStringValue(value, headerField);
+ } catch(NullPointerException e) {
+ log("null pointer error!");
+ } catch (RuntimeException e) {
+ log(headerField + "is not Encoded-String-Value header field!");
+ return null;
+ }
+ }
+ break;
+ }
+
+ /* Addressing model */
+ case PduHeaders.BCC:
+ case PduHeaders.CC:
+ case PduHeaders.TO:
+ {
+ EncodedStringValue value =
+ parseEncodedStringValue(pduDataStream);
+ if (null != value) {
+ byte[] address = value.getTextString();
+ if (null != address) {
+ String str = new String(address);
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: (to/cc/bcc) address: " + headerField
+ + " value: " + str);
+ }
+ int endIndex = str.indexOf("/");
+ if (endIndex > 0) {
+ str = str.substring(0, endIndex);
+ }
+ try {
+ value.setTextString(str.getBytes());
+ } catch(NullPointerException e) {
+ log("null pointer error!");
+ return null;
+ }
+ }
+
+ try {
+ headers.appendEncodedStringValue(value, headerField);
+ } catch(NullPointerException e) {
+ log("null pointer error!");
+ } catch(RuntimeException e) {
+ log(headerField + "is not Encoded-String-Value header field!");
+ return null;
+ }
+ }
+ break;
+ }
+
+ /* Value-length
+ * (Absolute-token Date-value | Relative-token Delta-seconds-value) */
+ case PduHeaders.DELIVERY_TIME:
+ case PduHeaders.EXPIRY:
+ case PduHeaders.REPLY_CHARGING_DEADLINE:
+ {
+ /* parse Value-length */
+ parseValueLength(pduDataStream);
+
+ /* Absolute-token or Relative-token */
+ int token = extractByteValue(pduDataStream);
+
+ /* Date-value or Delta-seconds-value */
+ long timeValue;
+ try {
+ timeValue = parseLongInteger(pduDataStream);
+ } catch(RuntimeException e) {
+ log(headerField + "is not Long-Integer header field!");
+ return null;
+ }
+ if (PduHeaders.VALUE_RELATIVE_TOKEN == token) {
+ /* need to convert the Delta-seconds-value
+ * into Date-value */
+ timeValue = System.currentTimeMillis()/1000 + timeValue;
+ }
+
+ try {
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: time value: " + headerField
+ + " value: " + timeValue);
+ }
+ headers.setLongInteger(timeValue, headerField);
+ } catch(RuntimeException e) {
+ log(headerField + "is not Long-Integer header field!");
+ return null;
+ }
+ break;
+ }
+
+ case PduHeaders.FROM: {
+ /* From-value =
+ * Value-length
+ * (Address-present-token Encoded-string-value | Insert-address-token)
+ */
+ EncodedStringValue from = null;
+ parseValueLength(pduDataStream); /* parse value-length */
+
+ /* Address-present-token or Insert-address-token */
+ int fromToken = extractByteValue(pduDataStream);
+
+ /* Address-present-token or Insert-address-token */
+ if (PduHeaders.FROM_ADDRESS_PRESENT_TOKEN == fromToken) {
+ /* Encoded-string-value */
+ from = parseEncodedStringValue(pduDataStream);
+ if (null != from) {
+ byte[] address = from.getTextString();
+ if (null != address) {
+ String str = new String(address);
+ int endIndex = str.indexOf("/");
+ if (endIndex > 0) {
+ str = str.substring(0, endIndex);
+ }
+ try {
+ from.setTextString(str.getBytes());
+ } catch(NullPointerException e) {
+ log("null pointer error!");
+ return null;
+ }
+ }
+ }
+ } else {
+ try {
+ from = new EncodedStringValue(
+ PduHeaders.FROM_INSERT_ADDRESS_TOKEN_STR.getBytes());
+ } catch(NullPointerException e) {
+ log(headerField + "is not Encoded-String-Value header field!");
+ return null;
+ }
+ }
+
+ try {
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: from address: " + headerField
+ + " value: " + from.getString());
+ }
+ headers.setEncodedStringValue(from, PduHeaders.FROM);
+ } catch(NullPointerException e) {
+ log("null pointer error!");
+ } catch(RuntimeException e) {
+ log(headerField + "is not Encoded-String-Value header field!");
+ return null;
+ }
+ break;
+ }
+
+ case PduHeaders.MESSAGE_CLASS: {
+ /* Message-class-value = Class-identifier | Token-text */
+ pduDataStream.mark(1);
+ int messageClass = extractByteValue(pduDataStream);
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: MESSAGE_CLASS: " + headerField
+ + " value: " + messageClass);
+ }
+
+ if (messageClass >= PduHeaders.MESSAGE_CLASS_PERSONAL) {
+ /* Class-identifier */
+ try {
+ if (PduHeaders.MESSAGE_CLASS_PERSONAL == messageClass) {
+ headers.setTextString(
+ PduHeaders.MESSAGE_CLASS_PERSONAL_STR.getBytes(),
+ PduHeaders.MESSAGE_CLASS);
+ } else if (PduHeaders.MESSAGE_CLASS_ADVERTISEMENT == messageClass) {
+ headers.setTextString(
+ PduHeaders.MESSAGE_CLASS_ADVERTISEMENT_STR.getBytes(),
+ PduHeaders.MESSAGE_CLASS);
+ } else if (PduHeaders.MESSAGE_CLASS_INFORMATIONAL == messageClass) {
+ headers.setTextString(
+ PduHeaders.MESSAGE_CLASS_INFORMATIONAL_STR.getBytes(),
+ PduHeaders.MESSAGE_CLASS);
+ } else if (PduHeaders.MESSAGE_CLASS_AUTO == messageClass) {
+ headers.setTextString(
+ PduHeaders.MESSAGE_CLASS_AUTO_STR.getBytes(),
+ PduHeaders.MESSAGE_CLASS);
+ }
+ } catch(NullPointerException e) {
+ log("null pointer error!");
+ } catch(RuntimeException e) {
+ log(headerField + "is not Text-String header field!");
+ return null;
+ }
+ } else {
+ /* Token-text */
+ pduDataStream.reset();
+ byte[] messageClassString = parseWapString(pduDataStream, TYPE_TEXT_STRING);
+ if (null != messageClassString) {
+ try {
+ headers.setTextString(messageClassString, PduHeaders.MESSAGE_CLASS);
+ } catch(NullPointerException e) {
+ log("null pointer error!");
+ } catch(RuntimeException e) {
+ log(headerField + "is not Text-String header field!");
+ return null;
+ }
+ }
+ }
+ break;
+ }
+
+ case PduHeaders.MMS_VERSION: {
+ int version = parseShortInteger(pduDataStream);
+
+ try {
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: MMS_VERSION: " + headerField
+ + " value: " + version);
+ }
+ headers.setOctet(version, PduHeaders.MMS_VERSION);
+ } catch(InvalidHeaderValueException e) {
+ log("Set invalid Octet value: " + version +
+ " into the header filed: " + headerField);
+ return null;
+ } catch(RuntimeException e) {
+ log(headerField + "is not Octet header field!");
+ return null;
+ }
+ break;
+ }
+
+ case PduHeaders.PREVIOUSLY_SENT_BY: {
+ /* Previously-sent-by-value =
+ * Value-length Forwarded-count-value Encoded-string-value */
+ /* parse value-length */
+ parseValueLength(pduDataStream);
+
+ /* parse Forwarded-count-value */
+ try {
+ parseIntegerValue(pduDataStream);
+ } catch(RuntimeException e) {
+ log(headerField + " is not Integer-Value");
+ return null;
+ }
+
+ /* parse Encoded-string-value */
+ EncodedStringValue previouslySentBy =
+ parseEncodedStringValue(pduDataStream);
+ if (null != previouslySentBy) {
+ try {
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: PREVIOUSLY_SENT_BY: " + headerField
+ + " value: " + previouslySentBy.getString());
+ }
+ headers.setEncodedStringValue(previouslySentBy,
+ PduHeaders.PREVIOUSLY_SENT_BY);
+ } catch(NullPointerException e) {
+ log("null pointer error!");
+ } catch(RuntimeException e) {
+ log(headerField + "is not Encoded-String-Value header field!");
+ return null;
+ }
+ }
+ break;
+ }
+
+ case PduHeaders.PREVIOUSLY_SENT_DATE: {
+ /* Previously-sent-date-value =
+ * Value-length Forwarded-count-value Date-value */
+ /* parse value-length */
+ parseValueLength(pduDataStream);
+
+ /* parse Forwarded-count-value */
+ try {
+ parseIntegerValue(pduDataStream);
+ } catch(RuntimeException e) {
+ log(headerField + " is not Integer-Value");
+ return null;
+ }
+
+ /* Date-value */
+ try {
+ long perviouslySentDate = parseLongInteger(pduDataStream);
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: PREVIOUSLY_SENT_DATE: " + headerField
+ + " value: " + perviouslySentDate);
+ }
+ headers.setLongInteger(perviouslySentDate,
+ PduHeaders.PREVIOUSLY_SENT_DATE);
+ } catch(RuntimeException e) {
+ log(headerField + "is not Long-Integer header field!");
+ return null;
+ }
+ break;
+ }
+
+ case PduHeaders.MM_FLAGS: {
+ /* MM-flags-value =
+ * Value-length
+ * ( Add-token | Remove-token | Filter-token )
+ * Encoded-string-value
+ */
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: MM_FLAGS: " + headerField
+ + " NOT REALLY SUPPORTED");
+ }
+
+ /* parse Value-length */
+ parseValueLength(pduDataStream);
+
+ /* Add-token | Remove-token | Filter-token */
+ extractByteValue(pduDataStream);
+
+ /* Encoded-string-value */
+ parseEncodedStringValue(pduDataStream);
+
+ /* not store this header filed in "headers",
+ * because now PduHeaders doesn't support it */
+ break;
+ }
+
+ /* Value-length
+ * (Message-total-token | Size-total-token) Integer-Value */
+ case PduHeaders.MBOX_TOTALS:
+ case PduHeaders.MBOX_QUOTAS:
+ {
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: MBOX_TOTALS: " + headerField);
+ }
+ /* Value-length */
+ parseValueLength(pduDataStream);
+
+ /* Message-total-token | Size-total-token */
+ extractByteValue(pduDataStream);
+
+ /*Integer-Value*/
+ try {
+ parseIntegerValue(pduDataStream);
+ } catch(RuntimeException e) {
+ log(headerField + " is not Integer-Value");
+ return null;
+ }
+
+ /* not store these headers filed in "headers",
+ because now PduHeaders doesn't support them */
+ break;
+ }
+
+ case PduHeaders.ELEMENT_DESCRIPTOR: {
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: ELEMENT_DESCRIPTOR: " + headerField);
+ }
+ parseContentType(pduDataStream, null);
+
+ /* not store this header filed in "headers",
+ because now PduHeaders doesn't support it */
+ break;
+ }
+
+ case PduHeaders.CONTENT_TYPE: {
+ HashMap<Integer, Object> map =
+ new HashMap<Integer, Object>();
+ byte[] contentType =
+ parseContentType(pduDataStream, map);
+
+ if (null != contentType) {
+ try {
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: CONTENT_TYPE: " + headerField +
+ contentType.toString());
+ }
+ headers.setTextString(contentType, PduHeaders.CONTENT_TYPE);
+ } catch(NullPointerException e) {
+ log("null pointer error!");
+ } catch(RuntimeException e) {
+ log(headerField + "is not Text-String header field!");
+ return null;
+ }
+ }
+
+ /* get start parameter */
+ mStartParam = (byte[]) map.get(PduPart.P_START);
+
+ /* get charset parameter */
+ mTypeParam= (byte[]) map.get(PduPart.P_TYPE);
+
+ keepParsing = false;
+ break;
+ }
+
+ case PduHeaders.CONTENT:
+ case PduHeaders.ADDITIONAL_HEADERS:
+ case PduHeaders.ATTRIBUTES:
+ default: {
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: Unknown header: " + headerField);
+ }
+ log("Unknown header");
+ }
+ }
+ }
+
+ return headers;
+ }
+
+ /**
+ * Parse pdu parts.
+ *
+ * @param pduDataStream pdu data input stream
+ * @return parts in PduBody structure
+ */
+ protected PduBody parseParts(ByteArrayInputStream pduDataStream) {
+ if (pduDataStream == null) {
+ return null;
+ }
+
+ int count = parseUnsignedInt(pduDataStream); // get the number of parts
+ PduBody body = new PduBody();
+
+ for (int i = 0 ; i < count ; i++) {
+ int headerLength = parseUnsignedInt(pduDataStream);
+ int dataLength = parseUnsignedInt(pduDataStream);
+ PduPart part = new PduPart();
+ int startPos = pduDataStream.available();
+ if (startPos <= 0) {
+ // Invalid part.
+ return null;
+ }
+
+ /* parse part's content-type */
+ HashMap<Integer, Object> map = new HashMap<Integer, Object>();
+ byte[] contentType = parseContentType(pduDataStream, map);
+ if (null != contentType) {
+ part.setContentType(contentType);
+ } else {
+ part.setContentType((PduContentTypes.contentTypes[0]).getBytes()); //"*/*"
+ }
+
+ /* get name parameter */
+ byte[] name = (byte[]) map.get(PduPart.P_NAME);
+ if (null != name) {
+ part.setName(name);
+ }
+
+ /* get charset parameter */
+ Integer charset = (Integer) map.get(PduPart.P_CHARSET);
+ if (null != charset) {
+ part.setCharset(charset);
+ }
+
+ /* parse part's headers */
+ int endPos = pduDataStream.available();
+ int partHeaderLen = headerLength - (startPos - endPos);
+ if (partHeaderLen > 0) {
+ if (false == parsePartHeaders(pduDataStream, part, partHeaderLen)) {
+ // Parse part header faild.
+ return null;
+ }
+ } else if (partHeaderLen < 0) {
+ // Invalid length of content-type.
+ return null;
+ }
+
+ /* FIXME: check content-id, name, filename and content location,
+ * if not set anyone of them, generate a default content-location
+ */
+ if ((null == part.getContentLocation())
+ && (null == part.getName())
+ && (null == part.getFilename())
+ && (null == part.getContentId())) {
+ part.setContentLocation(Long.toOctalString(
+ System.currentTimeMillis()).getBytes());
+ }
+
+ /* get part's data */
+ if (dataLength > 0) {
+ byte[] partData = new byte[dataLength];
+ String partContentType = new String(part.getContentType());
+ pduDataStream.read(partData, 0, dataLength);
+ if (partContentType.equalsIgnoreCase(ContentType.MULTIPART_ALTERNATIVE)) {
+ // parse "multipart/vnd.wap.multipart.alternative".
+ PduBody childBody = parseParts(new ByteArrayInputStream(partData));
+ // take the first part of children.
+ part = childBody.getPart(0);
+ } else {
+ // Check Content-Transfer-Encoding.
+ byte[] partDataEncoding = part.getContentTransferEncoding();
+ if (null != partDataEncoding) {
+ String encoding = new String(partDataEncoding);
+ if (encoding.equalsIgnoreCase(PduPart.P_BASE64)) {
+ // Decode "base64" into "binary".
+ partData = Base64.decodeBase64(partData);
+ } else if (encoding.equalsIgnoreCase(PduPart.P_QUOTED_PRINTABLE)) {
+ // Decode "quoted-printable" into "binary".
+ partData = QuotedPrintable.decodeQuotedPrintable(partData);
+ } else {
+ // "binary" is the default encoding.
+ }
+ }
+ if (null == partData) {
+ log("Decode part data error!");
+ return null;
+ }
+ part.setData(partData);
+ }
+ }
+
+ /* add this part to body */
+ if (THE_FIRST_PART == checkPartPosition(part)) {
+ /* this is the first part */
+ body.addPart(0, part);
+ } else {
+ /* add the part to the end */
+ body.addPart(part);
+ }
+ }
+
+ return body;
+ }
+
+ /**
+ * Log status.
+ *
+ * @param text log information
+ */
+ @UnsupportedAppUsage
+ private static void log(String text) {
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, text);
+ }
+ }
+
+ /**
+ * Parse unsigned integer.
+ *
+ * @param pduDataStream pdu data input stream
+ * @return the integer, -1 when failed
+ */
+ @UnsupportedAppUsage
+ protected static int parseUnsignedInt(ByteArrayInputStream pduDataStream) {
+ /**
+ * From wap-230-wsp-20010705-a.pdf
+ * The maximum size of a uintvar is 32 bits.
+ * So it will be encoded in no more than 5 octets.
+ */
+ assert(null != pduDataStream);
+ int result = 0;
+ int temp = pduDataStream.read();
+ if (temp == -1) {
+ return temp;
+ }
+
+ while((temp & 0x80) != 0) {
+ result = result << 7;
+ result |= temp & 0x7F;
+ temp = pduDataStream.read();
+ if (temp == -1) {
+ return temp;
+ }
+ }
+
+ result = result << 7;
+ result |= temp & 0x7F;
+
+ return result;
+ }
+
+ /**
+ * Parse value length.
+ *
+ * @param pduDataStream pdu data input stream
+ * @return the integer
+ */
+ @UnsupportedAppUsage
+ protected static int parseValueLength(ByteArrayInputStream pduDataStream) {
+ /**
+ * From wap-230-wsp-20010705-a.pdf
+ * Value-length = Short-length | (Length-quote Length)
+ * Short-length = <Any octet 0-30>
+ * Length-quote = <Octet 31>
+ * Length = Uintvar-integer
+ * Uintvar-integer = 1*5 OCTET
+ */
+ assert(null != pduDataStream);
+ int temp = pduDataStream.read();
+ assert(-1 != temp);
+ int first = temp & 0xFF;
+
+ if (first <= SHORT_LENGTH_MAX) {
+ return first;
+ } else if (first == LENGTH_QUOTE) {
+ return parseUnsignedInt(pduDataStream);
+ }
+
+ throw new RuntimeException ("Value length > LENGTH_QUOTE!");
+ }
+
+ /**
+ * Parse encoded string value.
+ *
+ * @param pduDataStream pdu data input stream
+ * @return the EncodedStringValue
+ */
+ protected static EncodedStringValue parseEncodedStringValue(ByteArrayInputStream pduDataStream){
+ /**
+ * From OMA-TS-MMS-ENC-V1_3-20050927-C.pdf
+ * Encoded-string-value = Text-string | Value-length Char-set Text-string
+ */
+ assert(null != pduDataStream);
+ pduDataStream.mark(1);
+ EncodedStringValue returnValue = null;
+ int charset = 0;
+ int temp = pduDataStream.read();
+ assert(-1 != temp);
+ int first = temp & 0xFF;
+ if (first == 0) {
+ return new EncodedStringValue("");
+ }
+
+ pduDataStream.reset();
+ if (first < TEXT_MIN) {
+ parseValueLength(pduDataStream);
+
+ charset = parseShortInteger(pduDataStream); //get the "Charset"
+ }
+
+ byte[] textString = parseWapString(pduDataStream, TYPE_TEXT_STRING);
+
+ try {
+ if (0 != charset) {
+ returnValue = new EncodedStringValue(charset, textString);
+ } else {
+ returnValue = new EncodedStringValue(textString);
+ }
+ } catch(Exception e) {
+ return null;
+ }
+
+ return returnValue;
+ }
+
+ /**
+ * Parse Text-String or Quoted-String.
+ *
+ * @param pduDataStream pdu data input stream
+ * @param stringType TYPE_TEXT_STRING or TYPE_QUOTED_STRING
+ * @return the string without End-of-string in byte array
+ */
+ @UnsupportedAppUsage
+ protected static byte[] parseWapString(ByteArrayInputStream pduDataStream,
+ int stringType) {
+ assert(null != pduDataStream);
+ /**
+ * From wap-230-wsp-20010705-a.pdf
+ * Text-string = [Quote] *TEXT End-of-string
+ * If the first character in the TEXT is in the range of 128-255,
+ * a Quote character must precede it.
+ * Otherwise the Quote character must be omitted.
+ * The Quote is not part of the contents.
+ * Quote = <Octet 127>
+ * End-of-string = <Octet 0>
+ *
+ * Quoted-string = <Octet 34> *TEXT End-of-string
+ *
+ * Token-text = Token End-of-string
+ */
+
+ // Mark supposed beginning of Text-string
+ // We will have to mark again if first char is QUOTE or QUOTED_STRING_FLAG
+ pduDataStream.mark(1);
+
+ // Check first char
+ int temp = pduDataStream.read();
+ assert(-1 != temp);
+ if ((TYPE_QUOTED_STRING == stringType) &&
+ (QUOTED_STRING_FLAG == temp)) {
+ // Mark again if QUOTED_STRING_FLAG and ignore it
+ pduDataStream.mark(1);
+ } else if ((TYPE_TEXT_STRING == stringType) &&
+ (QUOTE == temp)) {
+ // Mark again if QUOTE and ignore it
+ pduDataStream.mark(1);
+ } else {
+ // Otherwise go back to origin
+ pduDataStream.reset();
+ }
+
+ // We are now definitely at the beginning of string
+ /**
+ * Return *TOKEN or *TEXT (Text-String without QUOTE,
+ * Quoted-String without QUOTED_STRING_FLAG and without End-of-string)
+ */
+ return getWapString(pduDataStream, stringType);
+ }
+
+ /**
+ * Check TOKEN data defined in RFC2616.
+ * @param ch checking data
+ * @return true when ch is TOKEN, false when ch is not TOKEN
+ */
+ protected static boolean isTokenCharacter(int ch) {
+ /**
+ * Token = 1*<any CHAR except CTLs or separators>
+ * separators = "("(40) | ")"(41) | "<"(60) | ">"(62) | "@"(64)
+ * | ","(44) | ";"(59) | ":"(58) | "\"(92) | <">(34)
+ * | "/"(47) | "["(91) | "]"(93) | "?"(63) | "="(61)
+ * | "{"(123) | "}"(125) | SP(32) | HT(9)
+ * CHAR = <any US-ASCII character (octets 0 - 127)>
+ * CTL = <any US-ASCII control character
+ * (octets 0 - 31) and DEL (127)>
+ * SP = <US-ASCII SP, space (32)>
+ * HT = <US-ASCII HT, horizontal-tab (9)>
+ */
+ if((ch < 33) || (ch > 126)) {
+ return false;
+ }
+
+ switch(ch) {
+ case '"': /* '"' */
+ case '(': /* '(' */
+ case ')': /* ')' */
+ case ',': /* ',' */
+ case '/': /* '/' */
+ case ':': /* ':' */
+ case ';': /* ';' */
+ case '<': /* '<' */
+ case '=': /* '=' */
+ case '>': /* '>' */
+ case '?': /* '?' */
+ case '@': /* '@' */
+ case '[': /* '[' */
+ case '\\': /* '\' */
+ case ']': /* ']' */
+ case '{': /* '{' */
+ case '}': /* '}' */
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Check TEXT data defined in RFC2616.
+ * @param ch checking data
+ * @return true when ch is TEXT, false when ch is not TEXT
+ */
+ protected static boolean isText(int ch) {
+ /**
+ * TEXT = <any OCTET except CTLs,
+ * but including LWS>
+ * CTL = <any US-ASCII control character
+ * (octets 0 - 31) and DEL (127)>
+ * LWS = [CRLF] 1*( SP | HT )
+ * CRLF = CR LF
+ * CR = <US-ASCII CR, carriage return (13)>
+ * LF = <US-ASCII LF, linefeed (10)>
+ */
+ if(((ch >= 32) && (ch <= 126)) || ((ch >= 128) && (ch <= 255))) {
+ return true;
+ }
+
+ switch(ch) {
+ case '\t': /* '\t' */
+ case '\n': /* '\n' */
+ case '\r': /* '\r' */
+ return true;
+ }
+
+ return false;
+ }
+
+ protected static byte[] getWapString(ByteArrayInputStream pduDataStream,
+ int stringType) {
+ assert(null != pduDataStream);
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ int temp = pduDataStream.read();
+ assert(-1 != temp);
+ while((-1 != temp) && ('\0' != temp)) {
+ // check each of the character
+ if (stringType == TYPE_TOKEN_STRING) {
+ if (isTokenCharacter(temp)) {
+ out.write(temp);
+ }
+ } else {
+ if (isText(temp)) {
+ out.write(temp);
+ }
+ }
+
+ temp = pduDataStream.read();
+ assert(-1 != temp);
+ }
+
+ if (out.size() > 0) {
+ return out.toByteArray();
+ }
+
+ return null;
+ }
+
+ /**
+ * Extract a byte value from the input stream.
+ *
+ * @param pduDataStream pdu data input stream
+ * @return the byte
+ */
+ protected static int extractByteValue(ByteArrayInputStream pduDataStream) {
+ assert(null != pduDataStream);
+ int temp = pduDataStream.read();
+ assert(-1 != temp);
+ return temp & 0xFF;
+ }
+
+ /**
+ * Parse Short-Integer.
+ *
+ * @param pduDataStream pdu data input stream
+ * @return the byte
+ */
+ @UnsupportedAppUsage
+ protected static int parseShortInteger(ByteArrayInputStream pduDataStream) {
+ /**
+ * From wap-230-wsp-20010705-a.pdf
+ * Short-integer = OCTET
+ * Integers in range 0-127 shall be encoded as a one
+ * octet value with the most significant bit set to one (1xxx xxxx)
+ * and with the value in the remaining least significant bits.
+ */
+ assert(null != pduDataStream);
+ int temp = pduDataStream.read();
+ assert(-1 != temp);
+ return temp & 0x7F;
+ }
+
+ /**
+ * Parse Long-Integer.
+ *
+ * @param pduDataStream pdu data input stream
+ * @return long integer
+ */
+ protected static long parseLongInteger(ByteArrayInputStream pduDataStream) {
+ /**
+ * From wap-230-wsp-20010705-a.pdf
+ * Long-integer = Short-length Multi-octet-integer
+ * The Short-length indicates the length of the Multi-octet-integer
+ * Multi-octet-integer = 1*30 OCTET
+ * The content octets shall be an unsigned integer value
+ * with the most significant octet encoded first (big-endian representation).
+ * The minimum number of octets must be used to encode the value.
+ * Short-length = <Any octet 0-30>
+ */
+ assert(null != pduDataStream);
+ int temp = pduDataStream.read();
+ assert(-1 != temp);
+ int count = temp & 0xFF;
+
+ if (count > LONG_INTEGER_LENGTH_MAX) {
+ throw new RuntimeException("Octet count greater than 8 and I can't represent that!");
+ }
+
+ long result = 0;
+
+ for (int i = 0 ; i < count ; i++) {
+ temp = pduDataStream.read();
+ assert(-1 != temp);
+ result <<= 8;
+ result += (temp & 0xFF);
+ }
+
+ return result;
+ }
+
+ /**
+ * Parse Integer-Value.
+ *
+ * @param pduDataStream pdu data input stream
+ * @return long integer
+ */
+ protected static long parseIntegerValue(ByteArrayInputStream pduDataStream) {
+ /**
+ * From wap-230-wsp-20010705-a.pdf
+ * Integer-Value = Short-integer | Long-integer
+ */
+ assert(null != pduDataStream);
+ pduDataStream.mark(1);
+ int temp = pduDataStream.read();
+ assert(-1 != temp);
+ pduDataStream.reset();
+ if (temp > SHORT_INTEGER_MAX) {
+ return parseShortInteger(pduDataStream);
+ } else {
+ return parseLongInteger(pduDataStream);
+ }
+ }
+
+ /**
+ * To skip length of the wap value.
+ *
+ * @param pduDataStream pdu data input stream
+ * @param length area size
+ * @return the values in this area
+ */
+ protected static int skipWapValue(ByteArrayInputStream pduDataStream, int length) {
+ assert(null != pduDataStream);
+ byte[] area = new byte[length];
+ int readLen = pduDataStream.read(area, 0, length);
+ if (readLen < length) { //The actually read length is lower than the length
+ return -1;
+ } else {
+ return readLen;
+ }
+ }
+
+ /**
+ * Parse content type parameters. For now we just support
+ * four parameters used in mms: "type", "start", "name", "charset".
+ *
+ * @param pduDataStream pdu data input stream
+ * @param map to store parameters of Content-Type field
+ * @param length length of all the parameters
+ */
+ protected static void parseContentTypeParams(ByteArrayInputStream pduDataStream,
+ HashMap<Integer, Object> map, Integer length) {
+ /**
+ * From wap-230-wsp-20010705-a.pdf
+ * Parameter = Typed-parameter | Untyped-parameter
+ * Typed-parameter = Well-known-parameter-token Typed-value
+ * the actual expected type of the value is implied by the well-known parameter
+ * Well-known-parameter-token = Integer-value
+ * the code values used for parameters are specified in the Assigned Numbers appendix
+ * Typed-value = Compact-value | Text-value
+ * In addition to the expected type, there may be no value.
+ * If the value cannot be encoded using the expected type, it shall be encoded as text.
+ * Compact-value = Integer-value |
+ * Date-value | Delta-seconds-value | Q-value | Version-value |
+ * Uri-value
+ * Untyped-parameter = Token-text Untyped-value
+ * the type of the value is unknown, but it shall be encoded as an integer,
+ * if that is possible.
+ * Untyped-value = Integer-value | Text-value
+ */
+ assert(null != pduDataStream);
+ assert(length > 0);
+
+ int startPos = pduDataStream.available();
+ int tempPos = 0;
+ int lastLen = length;
+ while(0 < lastLen) {
+ int param = pduDataStream.read();
+ assert(-1 != param);
+ lastLen--;
+
+ switch (param) {
+ /**
+ * From rfc2387, chapter 3.1
+ * The type parameter must be specified and its value is the MIME media
+ * type of the "root" body part. It permits a MIME user agent to
+ * determine the content-type without reference to the enclosed body
+ * part. If the value of the type parameter and the root body part's
+ * content-type differ then the User Agent's behavior is undefined.
+ *
+ * From wap-230-wsp-20010705-a.pdf
+ * type = Constrained-encoding
+ * Constrained-encoding = Extension-Media | Short-integer
+ * Extension-media = *TEXT End-of-string
+ */
+ case PduPart.P_TYPE:
+ case PduPart.P_CT_MR_TYPE:
+ pduDataStream.mark(1);
+ int first = extractByteValue(pduDataStream);
+ pduDataStream.reset();
+ if (first > TEXT_MAX) {
+ // Short-integer (well-known type)
+ int index = parseShortInteger(pduDataStream);
+
+ if (index < PduContentTypes.contentTypes.length) {
+ byte[] type = (PduContentTypes.contentTypes[index]).getBytes();
+ map.put(PduPart.P_TYPE, type);
+ } else {
+ //not support this type, ignore it.
+ }
+ } else {
+ // Text-String (extension-media)
+ byte[] type = parseWapString(pduDataStream, TYPE_TEXT_STRING);
+ if ((null != type) && (null != map)) {
+ map.put(PduPart.P_TYPE, type);
+ }
+ }
+
+ tempPos = pduDataStream.available();
+ lastLen = length - (startPos - tempPos);
+ break;
+
+ /**
+ * From oma-ts-mms-conf-v1_3.pdf, chapter 10.2.3.
+ * Start Parameter Referring to Presentation
+ *
+ * From rfc2387, chapter 3.2
+ * The start parameter, if given, is the content-ID of the compound
+ * object's "root". If not present the "root" is the first body part in
+ * the Multipart/Related entity. The "root" is the element the
+ * applications processes first.
+ *
+ * From wap-230-wsp-20010705-a.pdf
+ * start = Text-String
+ */
+ case PduPart.P_START:
+ case PduPart.P_DEP_START:
+ byte[] start = parseWapString(pduDataStream, TYPE_TEXT_STRING);
+ if ((null != start) && (null != map)) {
+ map.put(PduPart.P_START, start);
+ }
+
+ tempPos = pduDataStream.available();
+ lastLen = length - (startPos - tempPos);
+ break;
+
+ /**
+ * From oma-ts-mms-conf-v1_3.pdf
+ * In creation, the character set SHALL be either us-ascii
+ * (IANA MIBenum 3) or utf-8 (IANA MIBenum 106)[Unicode].
+ * In retrieval, both us-ascii and utf-8 SHALL be supported.
+ *
+ * From wap-230-wsp-20010705-a.pdf
+ * charset = Well-known-charset|Text-String
+ * Well-known-charset = Any-charset | Integer-value
+ * Both are encoded using values from Character Set
+ * Assignments table in Assigned Numbers
+ * Any-charset = <Octet 128>
+ * Equivalent to the special RFC2616 charset value "*"
+ */
+ case PduPart.P_CHARSET:
+ pduDataStream.mark(1);
+ int firstValue = extractByteValue(pduDataStream);
+ pduDataStream.reset();
+ //Check first char
+ if (((firstValue > TEXT_MIN) && (firstValue < TEXT_MAX)) ||
+ (END_STRING_FLAG == firstValue)) {
+ //Text-String (extension-charset)
+ byte[] charsetStr = parseWapString(pduDataStream, TYPE_TEXT_STRING);
+ try {
+ int charsetInt = CharacterSets.getMibEnumValue(
+ new String(charsetStr));
+ map.put(PduPart.P_CHARSET, charsetInt);
+ } catch (UnsupportedEncodingException e) {
+ // Not a well-known charset, use "*".
+ Log.e(LOG_TAG, Arrays.toString(charsetStr), e);
+ map.put(PduPart.P_CHARSET, CharacterSets.ANY_CHARSET);
+ }
+ } else {
+ //Well-known-charset
+ int charset = (int) parseIntegerValue(pduDataStream);
+ if (map != null) {
+ map.put(PduPart.P_CHARSET, charset);
+ }
+ }
+
+ tempPos = pduDataStream.available();
+ lastLen = length - (startPos - tempPos);
+ break;
+
+ /**
+ * From oma-ts-mms-conf-v1_3.pdf
+ * A name for multipart object SHALL be encoded using name-parameter
+ * for Content-Type header in WSP multipart headers.
+ *
+ * From wap-230-wsp-20010705-a.pdf
+ * name = Text-String
+ */
+ case PduPart.P_DEP_NAME:
+ case PduPart.P_NAME:
+ byte[] name = parseWapString(pduDataStream, TYPE_TEXT_STRING);
+ if ((null != name) && (null != map)) {
+ map.put(PduPart.P_NAME, name);
+ }
+
+ tempPos = pduDataStream.available();
+ lastLen = length - (startPos - tempPos);
+ break;
+ default:
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "Not supported Content-Type parameter");
+ }
+ if (-1 == skipWapValue(pduDataStream, lastLen)) {
+ Log.e(LOG_TAG, "Corrupt Content-Type");
+ } else {
+ lastLen = 0;
+ }
+ break;
+ }
+ }
+
+ if (0 != lastLen) {
+ Log.e(LOG_TAG, "Corrupt Content-Type");
+ }
+ }
+
+ /**
+ * Parse content type.
+ *
+ * @param pduDataStream pdu data input stream
+ * @param map to store parameters in Content-Type header field
+ * @return Content-Type value
+ */
+ @UnsupportedAppUsage
+ protected static byte[] parseContentType(ByteArrayInputStream pduDataStream,
+ HashMap<Integer, Object> map) {
+ /**
+ * From wap-230-wsp-20010705-a.pdf
+ * Content-type-value = Constrained-media | Content-general-form
+ * Content-general-form = Value-length Media-type
+ * Media-type = (Well-known-media | Extension-Media) *(Parameter)
+ */
+ assert(null != pduDataStream);
+
+ byte[] contentType = null;
+ pduDataStream.mark(1);
+ int temp = pduDataStream.read();
+ assert(-1 != temp);
+ pduDataStream.reset();
+
+ int cur = (temp & 0xFF);
+
+ if (cur < TEXT_MIN) {
+ int length = parseValueLength(pduDataStream);
+ int startPos = pduDataStream.available();
+ pduDataStream.mark(1);
+ temp = pduDataStream.read();
+ assert(-1 != temp);
+ pduDataStream.reset();
+ int first = (temp & 0xFF);
+
+ if ((first >= TEXT_MIN) && (first <= TEXT_MAX)) {
+ contentType = parseWapString(pduDataStream, TYPE_TEXT_STRING);
+ } else if (first > TEXT_MAX) {
+ int index = parseShortInteger(pduDataStream);
+
+ if (index < PduContentTypes.contentTypes.length) { //well-known type
+ contentType = (PduContentTypes.contentTypes[index]).getBytes();
+ } else {
+ pduDataStream.reset();
+ contentType = parseWapString(pduDataStream, TYPE_TEXT_STRING);
+ }
+ } else {
+ Log.e(LOG_TAG, "Corrupt content-type");
+ return (PduContentTypes.contentTypes[0]).getBytes(); //"*/*"
+ }
+
+ int endPos = pduDataStream.available();
+ int parameterLen = length - (startPos - endPos);
+ if (parameterLen > 0) {//have parameters
+ parseContentTypeParams(pduDataStream, map, parameterLen);
+ }
+
+ if (parameterLen < 0) {
+ Log.e(LOG_TAG, "Corrupt MMS message");
+ return (PduContentTypes.contentTypes[0]).getBytes(); //"*/*"
+ }
+ } else if (cur <= TEXT_MAX) {
+ contentType = parseWapString(pduDataStream, TYPE_TEXT_STRING);
+ } else {
+ contentType =
+ (PduContentTypes.contentTypes[parseShortInteger(pduDataStream)]).getBytes();
+ }
+
+ return contentType;
+ }
+
+ /**
+ * Parse part's headers.
+ *
+ * @param pduDataStream pdu data input stream
+ * @param part to store the header informations of the part
+ * @param length length of the headers
+ * @return true if parse successfully, false otherwise
+ */
+ @UnsupportedAppUsage
+ protected boolean parsePartHeaders(ByteArrayInputStream pduDataStream,
+ PduPart part, int length) {
+ assert(null != pduDataStream);
+ assert(null != part);
+ assert(length > 0);
+
+ /**
+ * From oma-ts-mms-conf-v1_3.pdf, chapter 10.2.
+ * A name for multipart object SHALL be encoded using name-parameter
+ * for Content-Type header in WSP multipart headers.
+ * In decoding, name-parameter of Content-Type SHALL be used if available.
+ * If name-parameter of Content-Type is not available,
+ * filename parameter of Content-Disposition header SHALL be used if available.
+ * If neither name-parameter of Content-Type header nor filename parameter
+ * of Content-Disposition header is available,
+ * Content-Location header SHALL be used if available.
+ *
+ * Within SMIL part the reference to the media object parts SHALL use
+ * either Content-ID or Content-Location mechanism [RFC2557]
+ * and the corresponding WSP part headers in media object parts
+ * contain the corresponding definitions.
+ */
+ int startPos = pduDataStream.available();
+ int tempPos = 0;
+ int lastLen = length;
+ while(0 < lastLen) {
+ int header = pduDataStream.read();
+ assert(-1 != header);
+ lastLen--;
+
+ if (header > TEXT_MAX) {
+ // Number assigned headers.
+ switch (header) {
+ case PduPart.P_CONTENT_LOCATION:
+ /**
+ * From wap-230-wsp-20010705-a.pdf, chapter 8.4.2.21
+ * Content-location-value = Uri-value
+ */
+ byte[] contentLocation = parseWapString(pduDataStream, TYPE_TEXT_STRING);
+ if (null != contentLocation) {
+ part.setContentLocation(contentLocation);
+ }
+
+ tempPos = pduDataStream.available();
+ lastLen = length - (startPos - tempPos);
+ break;
+ case PduPart.P_CONTENT_ID:
+ /**
+ * From wap-230-wsp-20010705-a.pdf, chapter 8.4.2.21
+ * Content-ID-value = Quoted-string
+ */
+ byte[] contentId = parseWapString(pduDataStream, TYPE_QUOTED_STRING);
+ if (null != contentId) {
+ part.setContentId(contentId);
+ }
+
+ tempPos = pduDataStream.available();
+ lastLen = length - (startPos - tempPos);
+ break;
+ case PduPart.P_DEP_CONTENT_DISPOSITION:
+ case PduPart.P_CONTENT_DISPOSITION:
+ /**
+ * From wap-230-wsp-20010705-a.pdf, chapter 8.4.2.21
+ * Content-disposition-value = Value-length Disposition *(Parameter)
+ * Disposition = Form-data | Attachment | Inline | Token-text
+ * Form-data = <Octet 128>
+ * Attachment = <Octet 129>
+ * Inline = <Octet 130>
+ */
+
+ /*
+ * some carrier mmsc servers do not support content_disposition
+ * field correctly
+ */
+ if (mParseContentDisposition) {
+ int len = parseValueLength(pduDataStream);
+ pduDataStream.mark(1);
+ int thisStartPos = pduDataStream.available();
+ int thisEndPos = 0;
+ int value = pduDataStream.read();
+
+ if (value == PduPart.P_DISPOSITION_FROM_DATA ) {
+ part.setContentDisposition(PduPart.DISPOSITION_FROM_DATA);
+ } else if (value == PduPart.P_DISPOSITION_ATTACHMENT) {
+ part.setContentDisposition(PduPart.DISPOSITION_ATTACHMENT);
+ } else if (value == PduPart.P_DISPOSITION_INLINE) {
+ part.setContentDisposition(PduPart.DISPOSITION_INLINE);
+ } else {
+ pduDataStream.reset();
+ /* Token-text */
+ part.setContentDisposition(parseWapString(pduDataStream
+ , TYPE_TEXT_STRING));
+ }
+
+ /* get filename parameter and skip other parameters */
+ thisEndPos = pduDataStream.available();
+ if (thisStartPos - thisEndPos < len) {
+ value = pduDataStream.read();
+ if (value == PduPart.P_FILENAME) { //filename is text-string
+ part.setFilename(parseWapString(pduDataStream
+ , TYPE_TEXT_STRING));
+ }
+
+ /* skip other parameters */
+ thisEndPos = pduDataStream.available();
+ if (thisStartPos - thisEndPos < len) {
+ int last = len - (thisStartPos - thisEndPos);
+ byte[] temp = new byte[last];
+ pduDataStream.read(temp, 0, last);
+ }
+ }
+
+ tempPos = pduDataStream.available();
+ lastLen = length - (startPos - tempPos);
+ }
+ break;
+ default:
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "Not supported Part headers: " + header);
+ }
+ if (-1 == skipWapValue(pduDataStream, lastLen)) {
+ Log.e(LOG_TAG, "Corrupt Part headers");
+ return false;
+ }
+ lastLen = 0;
+ break;
+ }
+ } else if ((header >= TEXT_MIN) && (header <= TEXT_MAX)) {
+ // Not assigned header.
+ byte[] tempHeader = parseWapString(pduDataStream, TYPE_TEXT_STRING);
+ byte[] tempValue = parseWapString(pduDataStream, TYPE_TEXT_STRING);
+
+ // Check the header whether it is "Content-Transfer-Encoding".
+ if (true ==
+ PduPart.CONTENT_TRANSFER_ENCODING.equalsIgnoreCase(new String(tempHeader))) {
+ part.setContentTransferEncoding(tempValue);
+ }
+
+ tempPos = pduDataStream.available();
+ lastLen = length - (startPos - tempPos);
+ } else {
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "Not supported Part headers: " + header);
+ }
+ // Skip all headers of this part.
+ if (-1 == skipWapValue(pduDataStream, lastLen)) {
+ Log.e(LOG_TAG, "Corrupt Part headers");
+ return false;
+ }
+ lastLen = 0;
+ }
+ }
+
+ if (0 != lastLen) {
+ Log.e(LOG_TAG, "Corrupt Part headers");
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Check the position of a specified part.
+ *
+ * @param part the part to be checked
+ * @return part position, THE_FIRST_PART when it's the
+ * first one, THE_LAST_PART when it's the last one.
+ */
+ @UnsupportedAppUsage
+ private static int checkPartPosition(PduPart part) {
+ assert(null != part);
+ if ((null == mTypeParam) &&
+ (null == mStartParam)) {
+ return THE_LAST_PART;
+ }
+
+ /* check part's content-id */
+ if (null != mStartParam) {
+ byte[] contentId = part.getContentId();
+ if (null != contentId) {
+ if (true == Arrays.equals(mStartParam, contentId)) {
+ return THE_FIRST_PART;
+ }
+ }
+ // This is not the first part, so append to end (keeping the original order)
+ // Check b/19607294 for details of this change
+ return THE_LAST_PART;
+ }
+
+ /* check part's content-type */
+ if (null != mTypeParam) {
+ byte[] contentType = part.getContentType();
+ if (null != contentType) {
+ if (true == Arrays.equals(mTypeParam, contentType)) {
+ return THE_FIRST_PART;
+ }
+ }
+ }
+
+ return THE_LAST_PART;
+ }
+
+ /**
+ * Check mandatory headers of a pdu.
+ *
+ * @param headers pdu headers
+ * @return true if the pdu has all of the mandatory headers, false otherwise.
+ */
+ protected static boolean checkMandatoryHeader(PduHeaders headers) {
+ if (null == headers) {
+ return false;
+ }
+
+ /* get message type */
+ int messageType = headers.getOctet(PduHeaders.MESSAGE_TYPE);
+
+ /* check Mms-Version field */
+ int mmsVersion = headers.getOctet(PduHeaders.MMS_VERSION);
+ if (0 == mmsVersion) {
+ // Every message should have Mms-Version field.
+ return false;
+ }
+
+ /* check mandatory header fields */
+ switch (messageType) {
+ case PduHeaders.MESSAGE_TYPE_SEND_REQ:
+ // Content-Type field.
+ byte[] srContentType = headers.getTextString(PduHeaders.CONTENT_TYPE);
+ if (null == srContentType) {
+ return false;
+ }
+
+ // From field.
+ EncodedStringValue srFrom = headers.getEncodedStringValue(PduHeaders.FROM);
+ if (null == srFrom) {
+ return false;
+ }
+
+ // Transaction-Id field.
+ byte[] srTransactionId = headers.getTextString(PduHeaders.TRANSACTION_ID);
+ if (null == srTransactionId) {
+ return false;
+ }
+
+ break;
+ case PduHeaders.MESSAGE_TYPE_SEND_CONF:
+ // Response-Status field.
+ int scResponseStatus = headers.getOctet(PduHeaders.RESPONSE_STATUS);
+ if (0 == scResponseStatus) {
+ return false;
+ }
+
+ // Transaction-Id field.
+ byte[] scTransactionId = headers.getTextString(PduHeaders.TRANSACTION_ID);
+ if (null == scTransactionId) {
+ return false;
+ }
+
+ break;
+ case PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND:
+ // Content-Location field.
+ byte[] niContentLocation = headers.getTextString(PduHeaders.CONTENT_LOCATION);
+ if (null == niContentLocation) {
+ return false;
+ }
+
+ // Expiry field.
+ long niExpiry = headers.getLongInteger(PduHeaders.EXPIRY);
+ if (-1 == niExpiry) {
+ return false;
+ }
+
+ // Message-Class field.
+ byte[] niMessageClass = headers.getTextString(PduHeaders.MESSAGE_CLASS);
+ if (null == niMessageClass) {
+ return false;
+ }
+
+ // Message-Size field.
+ long niMessageSize = headers.getLongInteger(PduHeaders.MESSAGE_SIZE);
+ if (-1 == niMessageSize) {
+ return false;
+ }
+
+ // Transaction-Id field.
+ byte[] niTransactionId = headers.getTextString(PduHeaders.TRANSACTION_ID);
+ if (null == niTransactionId) {
+ return false;
+ }
+
+ break;
+ case PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND:
+ // Status field.
+ int nriStatus = headers.getOctet(PduHeaders.STATUS);
+ if (0 == nriStatus) {
+ return false;
+ }
+
+ // Transaction-Id field.
+ byte[] nriTransactionId = headers.getTextString(PduHeaders.TRANSACTION_ID);
+ if (null == nriTransactionId) {
+ return false;
+ }
+
+ break;
+ case PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF:
+ // Content-Type field.
+ byte[] rcContentType = headers.getTextString(PduHeaders.CONTENT_TYPE);
+ if (null == rcContentType) {
+ return false;
+ }
+
+ // Date field.
+ long rcDate = headers.getLongInteger(PduHeaders.DATE);
+ if (-1 == rcDate) {
+ return false;
+ }
+
+ break;
+ case PduHeaders.MESSAGE_TYPE_DELIVERY_IND:
+ // Date field.
+ long diDate = headers.getLongInteger(PduHeaders.DATE);
+ if (-1 == diDate) {
+ return false;
+ }
+
+ // Message-Id field.
+ byte[] diMessageId = headers.getTextString(PduHeaders.MESSAGE_ID);
+ if (null == diMessageId) {
+ return false;
+ }
+
+ // Status field.
+ int diStatus = headers.getOctet(PduHeaders.STATUS);
+ if (0 == diStatus) {
+ return false;
+ }
+
+ // To field.
+ EncodedStringValue[] diTo = headers.getEncodedStringValues(PduHeaders.TO);
+ if (null == diTo) {
+ return false;
+ }
+
+ break;
+ case PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND:
+ // Transaction-Id field.
+ byte[] aiTransactionId = headers.getTextString(PduHeaders.TRANSACTION_ID);
+ if (null == aiTransactionId) {
+ return false;
+ }
+
+ break;
+ case PduHeaders.MESSAGE_TYPE_READ_ORIG_IND:
+ // Date field.
+ long roDate = headers.getLongInteger(PduHeaders.DATE);
+ if (-1 == roDate) {
+ return false;
+ }
+
+ // From field.
+ EncodedStringValue roFrom = headers.getEncodedStringValue(PduHeaders.FROM);
+ if (null == roFrom) {
+ return false;
+ }
+
+ // Message-Id field.
+ byte[] roMessageId = headers.getTextString(PduHeaders.MESSAGE_ID);
+ if (null == roMessageId) {
+ return false;
+ }
+
+ // Read-Status field.
+ int roReadStatus = headers.getOctet(PduHeaders.READ_STATUS);
+ if (0 == roReadStatus) {
+ return false;
+ }
+
+ // To field.
+ EncodedStringValue[] roTo = headers.getEncodedStringValues(PduHeaders.TO);
+ if (null == roTo) {
+ return false;
+ }
+
+ break;
+ case PduHeaders.MESSAGE_TYPE_READ_REC_IND:
+ // From field.
+ EncodedStringValue rrFrom = headers.getEncodedStringValue(PduHeaders.FROM);
+ if (null == rrFrom) {
+ return false;
+ }
+
+ // Message-Id field.
+ byte[] rrMessageId = headers.getTextString(PduHeaders.MESSAGE_ID);
+ if (null == rrMessageId) {
+ return false;
+ }
+
+ // Read-Status field.
+ int rrReadStatus = headers.getOctet(PduHeaders.READ_STATUS);
+ if (0 == rrReadStatus) {
+ return false;
+ }
+
+ // To field.
+ EncodedStringValue[] rrTo = headers.getEncodedStringValues(PduHeaders.TO);
+ if (null == rrTo) {
+ return false;
+ }
+
+ break;
+ default:
+ // Parser doesn't support this message type in this version.
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/telephony/java/com/google/android/mms/pdu/PduPart.java b/telephony/java/com/google/android/mms/pdu/PduPart.java
new file mode 100644
index 000000000000..09b775118dc3
--- /dev/null
+++ b/telephony/java/com/google/android/mms/pdu/PduPart.java
@@ -0,0 +1,439 @@
+/*
+ * Copyright (C) 2007-2008 Esmertec AG.
+ * Copyright (C) 2007-2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.pdu;
+
+import android.net.Uri;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * The pdu part.
+ */
+public class PduPart {
+ /**
+ * Well-Known Parameters.
+ */
+ public static final int P_Q = 0x80;
+ public static final int P_CHARSET = 0x81;
+ public static final int P_LEVEL = 0x82;
+ public static final int P_TYPE = 0x83;
+ public static final int P_DEP_NAME = 0x85;
+ public static final int P_DEP_FILENAME = 0x86;
+ public static final int P_DIFFERENCES = 0x87;
+ public static final int P_PADDING = 0x88;
+ // This value of "TYPE" s used with Content-Type: multipart/related
+ public static final int P_CT_MR_TYPE = 0x89;
+ public static final int P_DEP_START = 0x8A;
+ public static final int P_DEP_START_INFO = 0x8B;
+ public static final int P_DEP_COMMENT = 0x8C;
+ public static final int P_DEP_DOMAIN = 0x8D;
+ public static final int P_MAX_AGE = 0x8E;
+ public static final int P_DEP_PATH = 0x8F;
+ public static final int P_SECURE = 0x90;
+ public static final int P_SEC = 0x91;
+ public static final int P_MAC = 0x92;
+ public static final int P_CREATION_DATE = 0x93;
+ public static final int P_MODIFICATION_DATE = 0x94;
+ public static final int P_READ_DATE = 0x95;
+ public static final int P_SIZE = 0x96;
+ public static final int P_NAME = 0x97;
+ public static final int P_FILENAME = 0x98;
+ public static final int P_START = 0x99;
+ public static final int P_START_INFO = 0x9A;
+ public static final int P_COMMENT = 0x9B;
+ public static final int P_DOMAIN = 0x9C;
+ public static final int P_PATH = 0x9D;
+
+ /**
+ * Header field names.
+ */
+ public static final int P_CONTENT_TYPE = 0x91;
+ public static final int P_CONTENT_LOCATION = 0x8E;
+ public static final int P_CONTENT_ID = 0xC0;
+ public static final int P_DEP_CONTENT_DISPOSITION = 0xAE;
+ public static final int P_CONTENT_DISPOSITION = 0xC5;
+ // The next header is unassigned header, use reserved header(0x48) value.
+ public static final int P_CONTENT_TRANSFER_ENCODING = 0xC8;
+
+ /**
+ * Content=Transfer-Encoding string.
+ */
+ public static final String CONTENT_TRANSFER_ENCODING =
+ "Content-Transfer-Encoding";
+
+ /**
+ * Value of Content-Transfer-Encoding.
+ */
+ public static final String P_BINARY = "binary";
+ public static final String P_7BIT = "7bit";
+ public static final String P_8BIT = "8bit";
+ public static final String P_BASE64 = "base64";
+ public static final String P_QUOTED_PRINTABLE = "quoted-printable";
+
+ /**
+ * Value of disposition can be set to PduPart when the value is octet in
+ * the PDU.
+ * "from-data" instead of Form-data<Octet 128>.
+ * "attachment" instead of Attachment<Octet 129>.
+ * "inline" instead of Inline<Octet 130>.
+ */
+ static final byte[] DISPOSITION_FROM_DATA = "from-data".getBytes();
+ static final byte[] DISPOSITION_ATTACHMENT = "attachment".getBytes();
+ static final byte[] DISPOSITION_INLINE = "inline".getBytes();
+
+ /**
+ * Content-Disposition value.
+ */
+ public static final int P_DISPOSITION_FROM_DATA = 0x80;
+ public static final int P_DISPOSITION_ATTACHMENT = 0x81;
+ public static final int P_DISPOSITION_INLINE = 0x82;
+
+ /**
+ * Header of part.
+ */
+ private Map<Integer, Object> mPartHeader = null;
+
+ /**
+ * Data uri.
+ */
+ private Uri mUri = null;
+
+ /**
+ * Part data.
+ */
+ private byte[] mPartData = null;
+
+ private static final String TAG = "PduPart";
+
+ /**
+ * Empty Constructor.
+ */
+ @UnsupportedAppUsage
+ public PduPart() {
+ mPartHeader = new HashMap<Integer, Object>();
+ }
+
+ /**
+ * Set part data. The data are stored as byte array.
+ *
+ * @param data the data
+ */
+ @UnsupportedAppUsage
+ public void setData(byte[] data) {
+ if(data == null) {
+ return;
+ }
+
+ mPartData = new byte[data.length];
+ System.arraycopy(data, 0, mPartData, 0, data.length);
+ }
+
+ /**
+ * @return A copy of the part data or null if the data wasn't set or
+ * the data is stored as Uri.
+ * @see #getDataUri
+ */
+ @UnsupportedAppUsage
+ public byte[] getData() {
+ if(mPartData == null) {
+ return null;
+ }
+
+ byte[] byteArray = new byte[mPartData.length];
+ System.arraycopy(mPartData, 0, byteArray, 0, mPartData.length);
+ return byteArray;
+ }
+
+ /**
+ * @return The length of the data, if this object have data, else 0.
+ */
+ @UnsupportedAppUsage
+ public int getDataLength() {
+ if(mPartData != null){
+ return mPartData.length;
+ } else {
+ return 0;
+ }
+ }
+
+
+ /**
+ * Set data uri. The data are stored as Uri.
+ *
+ * @param uri the uri
+ */
+ @UnsupportedAppUsage
+ public void setDataUri(Uri uri) {
+ mUri = uri;
+ }
+
+ /**
+ * @return The Uri of the part data or null if the data wasn't set or
+ * the data is stored as byte array.
+ * @see #getData
+ */
+ @UnsupportedAppUsage
+ public Uri getDataUri() {
+ return mUri;
+ }
+
+ /**
+ * Set Content-id value
+ *
+ * @param contentId the content-id value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setContentId(byte[] contentId) {
+ if((contentId == null) || (contentId.length == 0)) {
+ throw new IllegalArgumentException(
+ "Content-Id may not be null or empty.");
+ }
+
+ if ((contentId.length > 1)
+ && ((char) contentId[0] == '<')
+ && ((char) contentId[contentId.length - 1] == '>')) {
+ mPartHeader.put(P_CONTENT_ID, contentId);
+ return;
+ }
+
+ // Insert beginning '<' and trailing '>' for Content-Id.
+ byte[] buffer = new byte[contentId.length + 2];
+ buffer[0] = (byte) (0xff & '<');
+ buffer[buffer.length - 1] = (byte) (0xff & '>');
+ System.arraycopy(contentId, 0, buffer, 1, contentId.length);
+ mPartHeader.put(P_CONTENT_ID, buffer);
+ }
+
+ /**
+ * Get Content-id value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getContentId() {
+ return (byte[]) mPartHeader.get(P_CONTENT_ID);
+ }
+
+ /**
+ * Set Char-set value.
+ *
+ * @param charset the value
+ */
+ @UnsupportedAppUsage
+ public void setCharset(int charset) {
+ mPartHeader.put(P_CHARSET, charset);
+ }
+
+ /**
+ * Get Char-set value
+ *
+ * @return the charset value. Return 0 if charset was not set.
+ */
+ @UnsupportedAppUsage
+ public int getCharset() {
+ Integer charset = (Integer) mPartHeader.get(P_CHARSET);
+ if(charset == null) {
+ return 0;
+ } else {
+ return charset.intValue();
+ }
+ }
+
+ /**
+ * Set Content-Location value.
+ *
+ * @param contentLocation the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setContentLocation(byte[] contentLocation) {
+ if(contentLocation == null) {
+ throw new NullPointerException("null content-location");
+ }
+
+ mPartHeader.put(P_CONTENT_LOCATION, contentLocation);
+ }
+
+ /**
+ * Get Content-Location value.
+ *
+ * @return the value
+ * return PduPart.disposition[0] instead of <Octet 128> (Form-data).
+ * return PduPart.disposition[1] instead of <Octet 129> (Attachment).
+ * return PduPart.disposition[2] instead of <Octet 130> (Inline).
+ */
+ @UnsupportedAppUsage
+ public byte[] getContentLocation() {
+ return (byte[]) mPartHeader.get(P_CONTENT_LOCATION);
+ }
+
+ /**
+ * Set Content-Disposition value.
+ * Use PduPart.disposition[0] instead of <Octet 128> (Form-data).
+ * Use PduPart.disposition[1] instead of <Octet 129> (Attachment).
+ * Use PduPart.disposition[2] instead of <Octet 130> (Inline).
+ *
+ * @param contentDisposition the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setContentDisposition(byte[] contentDisposition) {
+ if(contentDisposition == null) {
+ throw new NullPointerException("null content-disposition");
+ }
+
+ mPartHeader.put(P_CONTENT_DISPOSITION, contentDisposition);
+ }
+
+ /**
+ * Get Content-Disposition value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getContentDisposition() {
+ return (byte[]) mPartHeader.get(P_CONTENT_DISPOSITION);
+ }
+
+ /**
+ * Set Content-Type value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setContentType(byte[] contentType) {
+ if(contentType == null) {
+ throw new NullPointerException("null content-type");
+ }
+
+ mPartHeader.put(P_CONTENT_TYPE, contentType);
+ }
+
+ /**
+ * Get Content-Type value of part.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getContentType() {
+ return (byte[]) mPartHeader.get(P_CONTENT_TYPE);
+ }
+
+ /**
+ * Set Content-Transfer-Encoding value
+ *
+ * @param contentId the content-id value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setContentTransferEncoding(byte[] contentTransferEncoding) {
+ if(contentTransferEncoding == null) {
+ throw new NullPointerException("null content-transfer-encoding");
+ }
+
+ mPartHeader.put(P_CONTENT_TRANSFER_ENCODING, contentTransferEncoding);
+ }
+
+ /**
+ * Get Content-Transfer-Encoding value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getContentTransferEncoding() {
+ return (byte[]) mPartHeader.get(P_CONTENT_TRANSFER_ENCODING);
+ }
+
+ /**
+ * Set Content-type parameter: name.
+ *
+ * @param name the name value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setName(byte[] name) {
+ if(null == name) {
+ throw new NullPointerException("null content-id");
+ }
+
+ mPartHeader.put(P_NAME, name);
+ }
+
+ /**
+ * Get content-type parameter: name.
+ *
+ * @return the name
+ */
+ @UnsupportedAppUsage
+ public byte[] getName() {
+ return (byte[]) mPartHeader.get(P_NAME);
+ }
+
+ /**
+ * Get Content-disposition parameter: filename
+ *
+ * @param fileName the filename value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setFilename(byte[] fileName) {
+ if(null == fileName) {
+ throw new NullPointerException("null content-id");
+ }
+
+ mPartHeader.put(P_FILENAME, fileName);
+ }
+
+ /**
+ * Set Content-disposition parameter: filename
+ *
+ * @return the filename
+ */
+ @UnsupportedAppUsage
+ public byte[] getFilename() {
+ return (byte[]) mPartHeader.get(P_FILENAME);
+ }
+
+ @UnsupportedAppUsage
+ public String generateLocation() {
+ // Assumption: At least one of the content-location / name / filename
+ // or content-id should be set. This is guaranteed by the PduParser
+ // for incoming messages and by MM composer for outgoing messages.
+ byte[] location = (byte[]) mPartHeader.get(P_NAME);
+ if(null == location) {
+ location = (byte[]) mPartHeader.get(P_FILENAME);
+
+ if (null == location) {
+ location = (byte[]) mPartHeader.get(P_CONTENT_LOCATION);
+ }
+ }
+
+ if (null == location) {
+ byte[] contentId = (byte[]) mPartHeader.get(P_CONTENT_ID);
+ return "cid:" + new String(contentId);
+ } else {
+ return new String(location);
+ }
+ }
+}
+
diff --git a/telephony/java/com/google/android/mms/pdu/PduPersister.java b/telephony/java/com/google/android/mms/pdu/PduPersister.java
new file mode 100755
index 000000000000..93f30657bf1b
--- /dev/null
+++ b/telephony/java/com/google/android/mms/pdu/PduPersister.java
@@ -0,0 +1,1573 @@
+/*
+ * Copyright (C) 2007-2008 Esmertec AG.
+ * Copyright (C) 2007-2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.pdu;
+
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.DatabaseUtils;
+import android.drm.DrmManagerClient;
+import android.net.Uri;
+import android.os.ParcelFileDescriptor;
+import android.provider.Telephony;
+import android.provider.Telephony.Mms;
+import android.provider.Telephony.Mms.Addr;
+import android.provider.Telephony.Mms.Part;
+import android.provider.Telephony.MmsSms;
+import android.provider.Telephony.MmsSms.PendingMessages;
+import android.provider.Telephony.Threads;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import com.google.android.mms.ContentType;
+import com.google.android.mms.InvalidHeaderValueException;
+import com.google.android.mms.MmsException;
+import com.google.android.mms.util.DownloadDrmHelper;
+import com.google.android.mms.util.DrmConvertSession;
+import com.google.android.mms.util.PduCache;
+import com.google.android.mms.util.PduCacheEntry;
+import com.google.android.mms.util.SqliteWrapper;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+/**
+ * This class is the high-level manager of PDU storage.
+ */
+public class PduPersister {
+ private static final String TAG = "PduPersister";
+ private static final boolean DEBUG = false;
+ private static final boolean LOCAL_LOGV = false;
+
+ private static final long DUMMY_THREAD_ID = Long.MAX_VALUE;
+
+ /**
+ * The uri of temporary drm objects.
+ */
+ public static final String TEMPORARY_DRM_OBJECT_URI =
+ "content://mms/" + Long.MAX_VALUE + "/part";
+ /**
+ * Indicate that we transiently failed to process a MM.
+ */
+ public static final int PROC_STATUS_TRANSIENT_FAILURE = 1;
+ /**
+ * Indicate that we permanently failed to process a MM.
+ */
+ public static final int PROC_STATUS_PERMANENTLY_FAILURE = 2;
+ /**
+ * Indicate that we have successfully processed a MM.
+ */
+ public static final int PROC_STATUS_COMPLETED = 3;
+
+ private static PduPersister sPersister;
+ @UnsupportedAppUsage
+ private static final PduCache PDU_CACHE_INSTANCE;
+
+ @UnsupportedAppUsage
+ private static final int[] ADDRESS_FIELDS = new int[] {
+ PduHeaders.BCC,
+ PduHeaders.CC,
+ PduHeaders.FROM,
+ PduHeaders.TO
+ };
+
+ private static final String[] PDU_PROJECTION = new String[] {
+ Mms._ID,
+ Mms.MESSAGE_BOX,
+ Mms.THREAD_ID,
+ Mms.RETRIEVE_TEXT,
+ Mms.SUBJECT,
+ Mms.CONTENT_LOCATION,
+ Mms.CONTENT_TYPE,
+ Mms.MESSAGE_CLASS,
+ Mms.MESSAGE_ID,
+ Mms.RESPONSE_TEXT,
+ Mms.TRANSACTION_ID,
+ Mms.CONTENT_CLASS,
+ Mms.DELIVERY_REPORT,
+ Mms.MESSAGE_TYPE,
+ Mms.MMS_VERSION,
+ Mms.PRIORITY,
+ Mms.READ_REPORT,
+ Mms.READ_STATUS,
+ Mms.REPORT_ALLOWED,
+ Mms.RETRIEVE_STATUS,
+ Mms.STATUS,
+ Mms.DATE,
+ Mms.DELIVERY_TIME,
+ Mms.EXPIRY,
+ Mms.MESSAGE_SIZE,
+ Mms.SUBJECT_CHARSET,
+ Mms.RETRIEVE_TEXT_CHARSET,
+ };
+
+ private static final int PDU_COLUMN_ID = 0;
+ private static final int PDU_COLUMN_MESSAGE_BOX = 1;
+ private static final int PDU_COLUMN_THREAD_ID = 2;
+ private static final int PDU_COLUMN_RETRIEVE_TEXT = 3;
+ private static final int PDU_COLUMN_SUBJECT = 4;
+ private static final int PDU_COLUMN_CONTENT_LOCATION = 5;
+ private static final int PDU_COLUMN_CONTENT_TYPE = 6;
+ private static final int PDU_COLUMN_MESSAGE_CLASS = 7;
+ private static final int PDU_COLUMN_MESSAGE_ID = 8;
+ private static final int PDU_COLUMN_RESPONSE_TEXT = 9;
+ private static final int PDU_COLUMN_TRANSACTION_ID = 10;
+ private static final int PDU_COLUMN_CONTENT_CLASS = 11;
+ private static final int PDU_COLUMN_DELIVERY_REPORT = 12;
+ private static final int PDU_COLUMN_MESSAGE_TYPE = 13;
+ private static final int PDU_COLUMN_MMS_VERSION = 14;
+ private static final int PDU_COLUMN_PRIORITY = 15;
+ private static final int PDU_COLUMN_READ_REPORT = 16;
+ private static final int PDU_COLUMN_READ_STATUS = 17;
+ private static final int PDU_COLUMN_REPORT_ALLOWED = 18;
+ private static final int PDU_COLUMN_RETRIEVE_STATUS = 19;
+ private static final int PDU_COLUMN_STATUS = 20;
+ private static final int PDU_COLUMN_DATE = 21;
+ private static final int PDU_COLUMN_DELIVERY_TIME = 22;
+ private static final int PDU_COLUMN_EXPIRY = 23;
+ private static final int PDU_COLUMN_MESSAGE_SIZE = 24;
+ private static final int PDU_COLUMN_SUBJECT_CHARSET = 25;
+ private static final int PDU_COLUMN_RETRIEVE_TEXT_CHARSET = 26;
+
+ @UnsupportedAppUsage
+ private static final String[] PART_PROJECTION = new String[] {
+ Part._ID,
+ Part.CHARSET,
+ Part.CONTENT_DISPOSITION,
+ Part.CONTENT_ID,
+ Part.CONTENT_LOCATION,
+ Part.CONTENT_TYPE,
+ Part.FILENAME,
+ Part.NAME,
+ Part.TEXT
+ };
+
+ private static final int PART_COLUMN_ID = 0;
+ private static final int PART_COLUMN_CHARSET = 1;
+ private static final int PART_COLUMN_CONTENT_DISPOSITION = 2;
+ private static final int PART_COLUMN_CONTENT_ID = 3;
+ private static final int PART_COLUMN_CONTENT_LOCATION = 4;
+ private static final int PART_COLUMN_CONTENT_TYPE = 5;
+ private static final int PART_COLUMN_FILENAME = 6;
+ private static final int PART_COLUMN_NAME = 7;
+ private static final int PART_COLUMN_TEXT = 8;
+
+ @UnsupportedAppUsage
+ private static final HashMap<Uri, Integer> MESSAGE_BOX_MAP;
+ // These map are used for convenience in persist() and load().
+ private static final HashMap<Integer, Integer> CHARSET_COLUMN_INDEX_MAP;
+ private static final HashMap<Integer, Integer> ENCODED_STRING_COLUMN_INDEX_MAP;
+ private static final HashMap<Integer, Integer> TEXT_STRING_COLUMN_INDEX_MAP;
+ private static final HashMap<Integer, Integer> OCTET_COLUMN_INDEX_MAP;
+ private static final HashMap<Integer, Integer> LONG_COLUMN_INDEX_MAP;
+ @UnsupportedAppUsage
+ private static final HashMap<Integer, String> CHARSET_COLUMN_NAME_MAP;
+ @UnsupportedAppUsage
+ private static final HashMap<Integer, String> ENCODED_STRING_COLUMN_NAME_MAP;
+ @UnsupportedAppUsage
+ private static final HashMap<Integer, String> TEXT_STRING_COLUMN_NAME_MAP;
+ @UnsupportedAppUsage
+ private static final HashMap<Integer, String> OCTET_COLUMN_NAME_MAP;
+ @UnsupportedAppUsage
+ private static final HashMap<Integer, String> LONG_COLUMN_NAME_MAP;
+
+ static {
+ MESSAGE_BOX_MAP = new HashMap<Uri, Integer>();
+ MESSAGE_BOX_MAP.put(Mms.Inbox.CONTENT_URI, Mms.MESSAGE_BOX_INBOX);
+ MESSAGE_BOX_MAP.put(Mms.Sent.CONTENT_URI, Mms.MESSAGE_BOX_SENT);
+ MESSAGE_BOX_MAP.put(Mms.Draft.CONTENT_URI, Mms.MESSAGE_BOX_DRAFTS);
+ MESSAGE_BOX_MAP.put(Mms.Outbox.CONTENT_URI, Mms.MESSAGE_BOX_OUTBOX);
+
+ CHARSET_COLUMN_INDEX_MAP = new HashMap<Integer, Integer>();
+ CHARSET_COLUMN_INDEX_MAP.put(PduHeaders.SUBJECT, PDU_COLUMN_SUBJECT_CHARSET);
+ CHARSET_COLUMN_INDEX_MAP.put(PduHeaders.RETRIEVE_TEXT, PDU_COLUMN_RETRIEVE_TEXT_CHARSET);
+
+ CHARSET_COLUMN_NAME_MAP = new HashMap<Integer, String>();
+ CHARSET_COLUMN_NAME_MAP.put(PduHeaders.SUBJECT, Mms.SUBJECT_CHARSET);
+ CHARSET_COLUMN_NAME_MAP.put(PduHeaders.RETRIEVE_TEXT, Mms.RETRIEVE_TEXT_CHARSET);
+
+ // Encoded string field code -> column index/name map.
+ ENCODED_STRING_COLUMN_INDEX_MAP = new HashMap<Integer, Integer>();
+ ENCODED_STRING_COLUMN_INDEX_MAP.put(PduHeaders.RETRIEVE_TEXT, PDU_COLUMN_RETRIEVE_TEXT);
+ ENCODED_STRING_COLUMN_INDEX_MAP.put(PduHeaders.SUBJECT, PDU_COLUMN_SUBJECT);
+
+ ENCODED_STRING_COLUMN_NAME_MAP = new HashMap<Integer, String>();
+ ENCODED_STRING_COLUMN_NAME_MAP.put(PduHeaders.RETRIEVE_TEXT, Mms.RETRIEVE_TEXT);
+ ENCODED_STRING_COLUMN_NAME_MAP.put(PduHeaders.SUBJECT, Mms.SUBJECT);
+
+ // Text string field code -> column index/name map.
+ TEXT_STRING_COLUMN_INDEX_MAP = new HashMap<Integer, Integer>();
+ TEXT_STRING_COLUMN_INDEX_MAP.put(PduHeaders.CONTENT_LOCATION, PDU_COLUMN_CONTENT_LOCATION);
+ TEXT_STRING_COLUMN_INDEX_MAP.put(PduHeaders.CONTENT_TYPE, PDU_COLUMN_CONTENT_TYPE);
+ TEXT_STRING_COLUMN_INDEX_MAP.put(PduHeaders.MESSAGE_CLASS, PDU_COLUMN_MESSAGE_CLASS);
+ TEXT_STRING_COLUMN_INDEX_MAP.put(PduHeaders.MESSAGE_ID, PDU_COLUMN_MESSAGE_ID);
+ TEXT_STRING_COLUMN_INDEX_MAP.put(PduHeaders.RESPONSE_TEXT, PDU_COLUMN_RESPONSE_TEXT);
+ TEXT_STRING_COLUMN_INDEX_MAP.put(PduHeaders.TRANSACTION_ID, PDU_COLUMN_TRANSACTION_ID);
+
+ TEXT_STRING_COLUMN_NAME_MAP = new HashMap<Integer, String>();
+ TEXT_STRING_COLUMN_NAME_MAP.put(PduHeaders.CONTENT_LOCATION, Mms.CONTENT_LOCATION);
+ TEXT_STRING_COLUMN_NAME_MAP.put(PduHeaders.CONTENT_TYPE, Mms.CONTENT_TYPE);
+ TEXT_STRING_COLUMN_NAME_MAP.put(PduHeaders.MESSAGE_CLASS, Mms.MESSAGE_CLASS);
+ TEXT_STRING_COLUMN_NAME_MAP.put(PduHeaders.MESSAGE_ID, Mms.MESSAGE_ID);
+ TEXT_STRING_COLUMN_NAME_MAP.put(PduHeaders.RESPONSE_TEXT, Mms.RESPONSE_TEXT);
+ TEXT_STRING_COLUMN_NAME_MAP.put(PduHeaders.TRANSACTION_ID, Mms.TRANSACTION_ID);
+
+ // Octet field code -> column index/name map.
+ OCTET_COLUMN_INDEX_MAP = new HashMap<Integer, Integer>();
+ OCTET_COLUMN_INDEX_MAP.put(PduHeaders.CONTENT_CLASS, PDU_COLUMN_CONTENT_CLASS);
+ OCTET_COLUMN_INDEX_MAP.put(PduHeaders.DELIVERY_REPORT, PDU_COLUMN_DELIVERY_REPORT);
+ OCTET_COLUMN_INDEX_MAP.put(PduHeaders.MESSAGE_TYPE, PDU_COLUMN_MESSAGE_TYPE);
+ OCTET_COLUMN_INDEX_MAP.put(PduHeaders.MMS_VERSION, PDU_COLUMN_MMS_VERSION);
+ OCTET_COLUMN_INDEX_MAP.put(PduHeaders.PRIORITY, PDU_COLUMN_PRIORITY);
+ OCTET_COLUMN_INDEX_MAP.put(PduHeaders.READ_REPORT, PDU_COLUMN_READ_REPORT);
+ OCTET_COLUMN_INDEX_MAP.put(PduHeaders.READ_STATUS, PDU_COLUMN_READ_STATUS);
+ OCTET_COLUMN_INDEX_MAP.put(PduHeaders.REPORT_ALLOWED, PDU_COLUMN_REPORT_ALLOWED);
+ OCTET_COLUMN_INDEX_MAP.put(PduHeaders.RETRIEVE_STATUS, PDU_COLUMN_RETRIEVE_STATUS);
+ OCTET_COLUMN_INDEX_MAP.put(PduHeaders.STATUS, PDU_COLUMN_STATUS);
+
+ OCTET_COLUMN_NAME_MAP = new HashMap<Integer, String>();
+ OCTET_COLUMN_NAME_MAP.put(PduHeaders.CONTENT_CLASS, Mms.CONTENT_CLASS);
+ OCTET_COLUMN_NAME_MAP.put(PduHeaders.DELIVERY_REPORT, Mms.DELIVERY_REPORT);
+ OCTET_COLUMN_NAME_MAP.put(PduHeaders.MESSAGE_TYPE, Mms.MESSAGE_TYPE);
+ OCTET_COLUMN_NAME_MAP.put(PduHeaders.MMS_VERSION, Mms.MMS_VERSION);
+ OCTET_COLUMN_NAME_MAP.put(PduHeaders.PRIORITY, Mms.PRIORITY);
+ OCTET_COLUMN_NAME_MAP.put(PduHeaders.READ_REPORT, Mms.READ_REPORT);
+ OCTET_COLUMN_NAME_MAP.put(PduHeaders.READ_STATUS, Mms.READ_STATUS);
+ OCTET_COLUMN_NAME_MAP.put(PduHeaders.REPORT_ALLOWED, Mms.REPORT_ALLOWED);
+ OCTET_COLUMN_NAME_MAP.put(PduHeaders.RETRIEVE_STATUS, Mms.RETRIEVE_STATUS);
+ OCTET_COLUMN_NAME_MAP.put(PduHeaders.STATUS, Mms.STATUS);
+
+ // Long field code -> column index/name map.
+ LONG_COLUMN_INDEX_MAP = new HashMap<Integer, Integer>();
+ LONG_COLUMN_INDEX_MAP.put(PduHeaders.DATE, PDU_COLUMN_DATE);
+ LONG_COLUMN_INDEX_MAP.put(PduHeaders.DELIVERY_TIME, PDU_COLUMN_DELIVERY_TIME);
+ LONG_COLUMN_INDEX_MAP.put(PduHeaders.EXPIRY, PDU_COLUMN_EXPIRY);
+ LONG_COLUMN_INDEX_MAP.put(PduHeaders.MESSAGE_SIZE, PDU_COLUMN_MESSAGE_SIZE);
+
+ LONG_COLUMN_NAME_MAP = new HashMap<Integer, String>();
+ LONG_COLUMN_NAME_MAP.put(PduHeaders.DATE, Mms.DATE);
+ LONG_COLUMN_NAME_MAP.put(PduHeaders.DELIVERY_TIME, Mms.DELIVERY_TIME);
+ LONG_COLUMN_NAME_MAP.put(PduHeaders.EXPIRY, Mms.EXPIRY);
+ LONG_COLUMN_NAME_MAP.put(PduHeaders.MESSAGE_SIZE, Mms.MESSAGE_SIZE);
+
+ PDU_CACHE_INSTANCE = PduCache.getInstance();
+ }
+
+ @UnsupportedAppUsage
+ private final Context mContext;
+ @UnsupportedAppUsage
+ private final ContentResolver mContentResolver;
+ private final DrmManagerClient mDrmManagerClient;
+ @UnsupportedAppUsage
+ private final TelephonyManager mTelephonyManager;
+
+ private PduPersister(Context context) {
+ mContext = context;
+ mContentResolver = context.getContentResolver();
+ mDrmManagerClient = new DrmManagerClient(context);
+ mTelephonyManager = (TelephonyManager)context
+ .getSystemService(Context.TELEPHONY_SERVICE);
+ }
+
+ /** Get(or create if not exist) an instance of PduPersister */
+ @UnsupportedAppUsage
+ public static PduPersister getPduPersister(Context context) {
+ if ((sPersister == null)) {
+ sPersister = new PduPersister(context);
+ } else if (!context.equals(sPersister.mContext)) {
+ sPersister.release();
+ sPersister = new PduPersister(context);
+ }
+
+ return sPersister;
+ }
+
+ private void setEncodedStringValueToHeaders(
+ Cursor c, int columnIndex,
+ PduHeaders headers, int mapColumn) {
+ String s = c.getString(columnIndex);
+ if ((s != null) && (s.length() > 0)) {
+ int charsetColumnIndex = CHARSET_COLUMN_INDEX_MAP.get(mapColumn);
+ int charset = c.getInt(charsetColumnIndex);
+ EncodedStringValue value = new EncodedStringValue(
+ charset, getBytes(s));
+ headers.setEncodedStringValue(value, mapColumn);
+ }
+ }
+
+ private void setTextStringToHeaders(
+ Cursor c, int columnIndex,
+ PduHeaders headers, int mapColumn) {
+ String s = c.getString(columnIndex);
+ if (s != null) {
+ headers.setTextString(getBytes(s), mapColumn);
+ }
+ }
+
+ private void setOctetToHeaders(
+ Cursor c, int columnIndex,
+ PduHeaders headers, int mapColumn) throws InvalidHeaderValueException {
+ if (!c.isNull(columnIndex)) {
+ int b = c.getInt(columnIndex);
+ headers.setOctet(b, mapColumn);
+ }
+ }
+
+ private void setLongToHeaders(
+ Cursor c, int columnIndex,
+ PduHeaders headers, int mapColumn) {
+ if (!c.isNull(columnIndex)) {
+ long l = c.getLong(columnIndex);
+ headers.setLongInteger(l, mapColumn);
+ }
+ }
+
+ @UnsupportedAppUsage
+ private Integer getIntegerFromPartColumn(Cursor c, int columnIndex) {
+ if (!c.isNull(columnIndex)) {
+ return c.getInt(columnIndex);
+ }
+ return null;
+ }
+
+ @UnsupportedAppUsage
+ private byte[] getByteArrayFromPartColumn(Cursor c, int columnIndex) {
+ if (!c.isNull(columnIndex)) {
+ return getBytes(c.getString(columnIndex));
+ }
+ return null;
+ }
+
+ private PduPart[] loadParts(long msgId) throws MmsException {
+ Cursor c = SqliteWrapper.query(mContext, mContentResolver,
+ Uri.parse("content://mms/" + msgId + "/part"),
+ PART_PROJECTION, null, null, null);
+
+ PduPart[] parts = null;
+
+ try {
+ if ((c == null) || (c.getCount() == 0)) {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "loadParts(" + msgId + "): no part to load.");
+ }
+ return null;
+ }
+
+ int partCount = c.getCount();
+ int partIdx = 0;
+ parts = new PduPart[partCount];
+ while (c.moveToNext()) {
+ PduPart part = new PduPart();
+ Integer charset = getIntegerFromPartColumn(
+ c, PART_COLUMN_CHARSET);
+ if (charset != null) {
+ part.setCharset(charset);
+ }
+
+ byte[] contentDisposition = getByteArrayFromPartColumn(
+ c, PART_COLUMN_CONTENT_DISPOSITION);
+ if (contentDisposition != null) {
+ part.setContentDisposition(contentDisposition);
+ }
+
+ byte[] contentId = getByteArrayFromPartColumn(
+ c, PART_COLUMN_CONTENT_ID);
+ if (contentId != null) {
+ part.setContentId(contentId);
+ }
+
+ byte[] contentLocation = getByteArrayFromPartColumn(
+ c, PART_COLUMN_CONTENT_LOCATION);
+ if (contentLocation != null) {
+ part.setContentLocation(contentLocation);
+ }
+
+ byte[] contentType = getByteArrayFromPartColumn(
+ c, PART_COLUMN_CONTENT_TYPE);
+ if (contentType != null) {
+ part.setContentType(contentType);
+ } else {
+ throw new MmsException("Content-Type must be set.");
+ }
+
+ byte[] fileName = getByteArrayFromPartColumn(
+ c, PART_COLUMN_FILENAME);
+ if (fileName != null) {
+ part.setFilename(fileName);
+ }
+
+ byte[] name = getByteArrayFromPartColumn(
+ c, PART_COLUMN_NAME);
+ if (name != null) {
+ part.setName(name);
+ }
+
+ // Construct a Uri for this part.
+ long partId = c.getLong(PART_COLUMN_ID);
+ Uri partURI = Uri.parse("content://mms/part/" + partId);
+ part.setDataUri(partURI);
+
+ // For images/audio/video, we won't keep their data in Part
+ // because their renderer accept Uri as source.
+ String type = toIsoString(contentType);
+ if (!ContentType.isImageType(type)
+ && !ContentType.isAudioType(type)
+ && !ContentType.isVideoType(type)) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ InputStream is = null;
+
+ // Store simple string values directly in the database instead of an
+ // external file. This makes the text searchable and retrieval slightly
+ // faster.
+ if (ContentType.TEXT_PLAIN.equals(type) || ContentType.APP_SMIL.equals(type)
+ || ContentType.TEXT_HTML.equals(type)) {
+ String text = c.getString(PART_COLUMN_TEXT);
+ byte [] blob = new EncodedStringValue(text != null ? text : "")
+ .getTextString();
+ baos.write(blob, 0, blob.length);
+ } else {
+
+ try {
+ is = mContentResolver.openInputStream(partURI);
+
+ byte[] buffer = new byte[256];
+ int len = is.read(buffer);
+ while (len >= 0) {
+ baos.write(buffer, 0, len);
+ len = is.read(buffer);
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to load part data", e);
+ c.close();
+ throw new MmsException(e);
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to close stream", e);
+ } // Ignore
+ }
+ }
+ }
+ part.setData(baos.toByteArray());
+ }
+ parts[partIdx++] = part;
+ }
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ }
+
+ return parts;
+ }
+
+ private void loadAddress(long msgId, PduHeaders headers) {
+ Cursor c = SqliteWrapper.query(mContext, mContentResolver,
+ Uri.parse("content://mms/" + msgId + "/addr"),
+ new String[] { Addr.ADDRESS, Addr.CHARSET, Addr.TYPE },
+ null, null, null);
+
+ if (c != null) {
+ try {
+ while (c.moveToNext()) {
+ String addr = c.getString(0);
+ if (!TextUtils.isEmpty(addr)) {
+ int addrType = c.getInt(2);
+ switch (addrType) {
+ case PduHeaders.FROM:
+ headers.setEncodedStringValue(
+ new EncodedStringValue(c.getInt(1), getBytes(addr)),
+ addrType);
+ break;
+ case PduHeaders.TO:
+ case PduHeaders.CC:
+ case PduHeaders.BCC:
+ headers.appendEncodedStringValue(
+ new EncodedStringValue(c.getInt(1), getBytes(addr)),
+ addrType);
+ break;
+ default:
+ Log.e(TAG, "Unknown address type: " + addrType);
+ break;
+ }
+ }
+ }
+ } finally {
+ c.close();
+ }
+ }
+ }
+
+ /**
+ * Load a PDU from storage by given Uri.
+ *
+ * @param uri The Uri of the PDU to be loaded.
+ * @return A generic PDU object, it may be cast to dedicated PDU.
+ * @throws MmsException Failed to load some fields of a PDU.
+ */
+ @UnsupportedAppUsage
+ public GenericPdu load(Uri uri) throws MmsException {
+ GenericPdu pdu = null;
+ PduCacheEntry cacheEntry = null;
+ int msgBox = 0;
+ long threadId = -1;
+ try {
+ synchronized(PDU_CACHE_INSTANCE) {
+ if (PDU_CACHE_INSTANCE.isUpdating(uri)) {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "load: " + uri + " blocked by isUpdating()");
+ }
+ try {
+ PDU_CACHE_INSTANCE.wait();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "load: ", e);
+ }
+ cacheEntry = PDU_CACHE_INSTANCE.get(uri);
+ if (cacheEntry != null) {
+ return cacheEntry.getPdu();
+ }
+ }
+ // Tell the cache to indicate to other callers that this item
+ // is currently being updated.
+ PDU_CACHE_INSTANCE.setUpdating(uri, true);
+ }
+
+ Cursor c = SqliteWrapper.query(mContext, mContentResolver, uri,
+ PDU_PROJECTION, null, null, null);
+ PduHeaders headers = new PduHeaders();
+ Set<Entry<Integer, Integer>> set;
+ long msgId = ContentUris.parseId(uri);
+
+ try {
+ if ((c == null) || (c.getCount() != 1) || !c.moveToFirst()) {
+ throw new MmsException("Bad uri: " + uri);
+ }
+
+ msgBox = c.getInt(PDU_COLUMN_MESSAGE_BOX);
+ threadId = c.getLong(PDU_COLUMN_THREAD_ID);
+
+ set = ENCODED_STRING_COLUMN_INDEX_MAP.entrySet();
+ for (Entry<Integer, Integer> e : set) {
+ setEncodedStringValueToHeaders(
+ c, e.getValue(), headers, e.getKey());
+ }
+
+ set = TEXT_STRING_COLUMN_INDEX_MAP.entrySet();
+ for (Entry<Integer, Integer> e : set) {
+ setTextStringToHeaders(
+ c, e.getValue(), headers, e.getKey());
+ }
+
+ set = OCTET_COLUMN_INDEX_MAP.entrySet();
+ for (Entry<Integer, Integer> e : set) {
+ setOctetToHeaders(
+ c, e.getValue(), headers, e.getKey());
+ }
+
+ set = LONG_COLUMN_INDEX_MAP.entrySet();
+ for (Entry<Integer, Integer> e : set) {
+ setLongToHeaders(
+ c, e.getValue(), headers, e.getKey());
+ }
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ }
+
+ // Check whether 'msgId' has been assigned a valid value.
+ if (msgId == -1L) {
+ throw new MmsException("Error! ID of the message: -1.");
+ }
+
+ // Load address information of the MM.
+ loadAddress(msgId, headers);
+
+ int msgType = headers.getOctet(PduHeaders.MESSAGE_TYPE);
+ PduBody body = new PduBody();
+
+ // For PDU which type is M_retrieve.conf or Send.req, we should
+ // load multiparts and put them into the body of the PDU.
+ if ((msgType == PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF)
+ || (msgType == PduHeaders.MESSAGE_TYPE_SEND_REQ)) {
+ PduPart[] parts = loadParts(msgId);
+ if (parts != null) {
+ int partsNum = parts.length;
+ for (int i = 0; i < partsNum; i++) {
+ body.addPart(parts[i]);
+ }
+ }
+ }
+
+ switch (msgType) {
+ case PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND:
+ pdu = new NotificationInd(headers);
+ break;
+ case PduHeaders.MESSAGE_TYPE_DELIVERY_IND:
+ pdu = new DeliveryInd(headers);
+ break;
+ case PduHeaders.MESSAGE_TYPE_READ_ORIG_IND:
+ pdu = new ReadOrigInd(headers);
+ break;
+ case PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF:
+ pdu = new RetrieveConf(headers, body);
+ break;
+ case PduHeaders.MESSAGE_TYPE_SEND_REQ:
+ pdu = new SendReq(headers, body);
+ break;
+ case PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND:
+ pdu = new AcknowledgeInd(headers);
+ break;
+ case PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND:
+ pdu = new NotifyRespInd(headers);
+ break;
+ case PduHeaders.MESSAGE_TYPE_READ_REC_IND:
+ pdu = new ReadRecInd(headers);
+ break;
+ case PduHeaders.MESSAGE_TYPE_SEND_CONF:
+ case PduHeaders.MESSAGE_TYPE_FORWARD_REQ:
+ case PduHeaders.MESSAGE_TYPE_FORWARD_CONF:
+ case PduHeaders.MESSAGE_TYPE_MBOX_STORE_REQ:
+ case PduHeaders.MESSAGE_TYPE_MBOX_STORE_CONF:
+ case PduHeaders.MESSAGE_TYPE_MBOX_VIEW_REQ:
+ case PduHeaders.MESSAGE_TYPE_MBOX_VIEW_CONF:
+ case PduHeaders.MESSAGE_TYPE_MBOX_UPLOAD_REQ:
+ case PduHeaders.MESSAGE_TYPE_MBOX_UPLOAD_CONF:
+ case PduHeaders.MESSAGE_TYPE_MBOX_DELETE_REQ:
+ case PduHeaders.MESSAGE_TYPE_MBOX_DELETE_CONF:
+ case PduHeaders.MESSAGE_TYPE_MBOX_DESCR:
+ case PduHeaders.MESSAGE_TYPE_DELETE_REQ:
+ case PduHeaders.MESSAGE_TYPE_DELETE_CONF:
+ case PduHeaders.MESSAGE_TYPE_CANCEL_REQ:
+ case PduHeaders.MESSAGE_TYPE_CANCEL_CONF:
+ throw new MmsException(
+ "Unsupported PDU type: " + Integer.toHexString(msgType));
+
+ default:
+ throw new MmsException(
+ "Unrecognized PDU type: " + Integer.toHexString(msgType));
+ }
+ } finally {
+ synchronized(PDU_CACHE_INSTANCE) {
+ if (pdu != null) {
+ assert(PDU_CACHE_INSTANCE.get(uri) == null);
+ // Update the cache entry with the real info
+ cacheEntry = new PduCacheEntry(pdu, msgBox, threadId);
+ PDU_CACHE_INSTANCE.put(uri, cacheEntry);
+ }
+ PDU_CACHE_INSTANCE.setUpdating(uri, false);
+ PDU_CACHE_INSTANCE.notifyAll(); // tell anybody waiting on this entry to go ahead
+ }
+ }
+ return pdu;
+ }
+
+ @UnsupportedAppUsage
+ private void persistAddress(
+ long msgId, int type, EncodedStringValue[] array) {
+ ContentValues values = new ContentValues(3);
+
+ for (EncodedStringValue addr : array) {
+ values.clear(); // Clear all values first.
+ values.put(Addr.ADDRESS, toIsoString(addr.getTextString()));
+ values.put(Addr.CHARSET, addr.getCharacterSet());
+ values.put(Addr.TYPE, type);
+
+ Uri uri = Uri.parse("content://mms/" + msgId + "/addr");
+ SqliteWrapper.insert(mContext, mContentResolver, uri, values);
+ }
+ }
+
+ @UnsupportedAppUsage
+ private static String getPartContentType(PduPart part) {
+ return part.getContentType() == null ? null : toIsoString(part.getContentType());
+ }
+
+ @UnsupportedAppUsage
+ public Uri persistPart(PduPart part, long msgId, HashMap<Uri, InputStream> preOpenedFiles)
+ throws MmsException {
+ Uri uri = Uri.parse("content://mms/" + msgId + "/part");
+ ContentValues values = new ContentValues(8);
+
+ int charset = part.getCharset();
+ if (charset != 0 ) {
+ values.put(Part.CHARSET, charset);
+ }
+
+ String contentType = getPartContentType(part);
+ if (contentType != null) {
+ // There is no "image/jpg" in Android (and it's an invalid mimetype).
+ // Change it to "image/jpeg"
+ if (ContentType.IMAGE_JPG.equals(contentType)) {
+ contentType = ContentType.IMAGE_JPEG;
+ }
+
+ values.put(Part.CONTENT_TYPE, contentType);
+ // To ensure the SMIL part is always the first part.
+ if (ContentType.APP_SMIL.equals(contentType)) {
+ values.put(Part.SEQ, -1);
+ }
+ } else {
+ throw new MmsException("MIME type of the part must be set.");
+ }
+
+ if (part.getFilename() != null) {
+ String fileName = new String(part.getFilename());
+ values.put(Part.FILENAME, fileName);
+ }
+
+ if (part.getName() != null) {
+ String name = new String(part.getName());
+ values.put(Part.NAME, name);
+ }
+
+ Object value = null;
+ if (part.getContentDisposition() != null) {
+ value = toIsoString(part.getContentDisposition());
+ values.put(Part.CONTENT_DISPOSITION, (String) value);
+ }
+
+ if (part.getContentId() != null) {
+ value = toIsoString(part.getContentId());
+ values.put(Part.CONTENT_ID, (String) value);
+ }
+
+ if (part.getContentLocation() != null) {
+ value = toIsoString(part.getContentLocation());
+ values.put(Part.CONTENT_LOCATION, (String) value);
+ }
+
+ Uri res = SqliteWrapper.insert(mContext, mContentResolver, uri, values);
+ if (res == null) {
+ throw new MmsException("Failed to persist part, return null.");
+ }
+
+ persistData(part, res, contentType, preOpenedFiles);
+ // After successfully store the data, we should update
+ // the dataUri of the part.
+ part.setDataUri(res);
+
+ return res;
+ }
+
+ /**
+ * Save data of the part into storage. The source data may be given
+ * by a byte[] or a Uri. If it's a byte[], directly save it
+ * into storage, otherwise load source data from the dataUri and then
+ * save it. If the data is an image, we may scale down it according
+ * to user preference.
+ *
+ * @param part The PDU part which contains data to be saved.
+ * @param uri The URI of the part.
+ * @param contentType The MIME type of the part.
+ * @param preOpenedFiles if not null, a map of preopened InputStreams for the parts.
+ * @throws MmsException Cannot find source data or error occurred
+ * while saving the data.
+ */
+ private void persistData(PduPart part, Uri uri,
+ String contentType, HashMap<Uri, InputStream> preOpenedFiles)
+ throws MmsException {
+ OutputStream os = null;
+ InputStream is = null;
+ DrmConvertSession drmConvertSession = null;
+ Uri dataUri = null;
+ String path = null;
+
+ try {
+ byte[] data = part.getData();
+ if (ContentType.TEXT_PLAIN.equals(contentType)
+ || ContentType.APP_SMIL.equals(contentType)
+ || ContentType.TEXT_HTML.equals(contentType)) {
+ ContentValues cv = new ContentValues();
+ if (data == null) {
+ data = new String("").getBytes(CharacterSets.DEFAULT_CHARSET_NAME);
+ }
+ cv.put(Telephony.Mms.Part.TEXT, new EncodedStringValue(data).getString());
+ if (mContentResolver.update(uri, cv, null, null) != 1) {
+ throw new MmsException("unable to update " + uri.toString());
+ }
+ } else {
+ boolean isDrm = DownloadDrmHelper.isDrmConvertNeeded(contentType);
+ if (isDrm) {
+ if (uri != null) {
+ try (ParcelFileDescriptor pfd =
+ mContentResolver.openFileDescriptor(uri, "r")) {
+ if (pfd.getStatSize() > 0) {
+ // we're not going to re-persist and re-encrypt an already
+ // converted drm file
+ return;
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Can't get file info for: " + part.getDataUri(), e);
+ }
+ }
+ // We haven't converted the file yet, start the conversion
+ drmConvertSession = DrmConvertSession.open(mContext, contentType);
+ if (drmConvertSession == null) {
+ throw new MmsException("Mimetype " + contentType +
+ " can not be converted.");
+ }
+ }
+ // uri can look like:
+ // content://mms/part/98
+ os = mContentResolver.openOutputStream(uri);
+ if (data == null) {
+ dataUri = part.getDataUri();
+ if ((dataUri == null) || (dataUri.equals(uri))) {
+ Log.w(TAG, "Can't find data for this part.");
+ return;
+ }
+ // dataUri can look like:
+ // content://com.google.android.gallery3d.provider/picasa/item/5720646660183715586
+ if (preOpenedFiles != null && preOpenedFiles.containsKey(dataUri)) {
+ is = preOpenedFiles.get(dataUri);
+ }
+ if (is == null) {
+ is = mContentResolver.openInputStream(dataUri);
+ }
+
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "Saving data to: " + uri);
+ }
+
+ byte[] buffer = new byte[8192];
+ for (int len = 0; (len = is.read(buffer)) != -1; ) {
+ if (!isDrm) {
+ os.write(buffer, 0, len);
+ } else {
+ byte[] convertedData = drmConvertSession.convert(buffer, len);
+ if (convertedData != null) {
+ os.write(convertedData, 0, convertedData.length);
+ } else {
+ throw new MmsException("Error converting drm data.");
+ }
+ }
+ }
+ } else {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "Saving data to: " + uri);
+ }
+ if (!isDrm) {
+ os.write(data);
+ } else {
+ dataUri = uri;
+ byte[] convertedData = drmConvertSession.convert(data, data.length);
+ if (convertedData != null) {
+ os.write(convertedData, 0, convertedData.length);
+ } else {
+ throw new MmsException("Error converting drm data.");
+ }
+ }
+ }
+ }
+ } catch (FileNotFoundException e) {
+ Log.e(TAG, "Failed to open Input/Output stream.", e);
+ throw new MmsException(e);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to read/write data.", e);
+ throw new MmsException(e);
+ } finally {
+ if (os != null) {
+ try {
+ os.close();
+ } catch (IOException e) {
+ Log.e(TAG, "IOException while closing: " + os, e);
+ } // Ignore
+ }
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ Log.e(TAG, "IOException while closing: " + is, e);
+ } // Ignore
+ }
+ if (drmConvertSession != null) {
+ drmConvertSession.close(path);
+
+ // Reset the permissions on the encrypted part file so everyone has only read
+ // permission.
+ File f = new File(path);
+ ContentValues values = new ContentValues(0);
+ SqliteWrapper.update(mContext, mContentResolver,
+ Uri.parse("content://mms/resetFilePerm/" + f.getName()),
+ values, null, null);
+ }
+ }
+ }
+
+ @UnsupportedAppUsage
+ private void updateAddress(
+ long msgId, int type, EncodedStringValue[] array) {
+ // Delete old address information and then insert new ones.
+ SqliteWrapper.delete(mContext, mContentResolver,
+ Uri.parse("content://mms/" + msgId + "/addr"),
+ Addr.TYPE + "=" + type, null);
+
+ persistAddress(msgId, type, array);
+ }
+
+ /**
+ * Update headers of a SendReq.
+ *
+ * @param uri The PDU which need to be updated.
+ * @param pdu New headers.
+ * @throws MmsException Bad URI or updating failed.
+ */
+ @UnsupportedAppUsage
+ public void updateHeaders(Uri uri, SendReq sendReq) {
+ synchronized(PDU_CACHE_INSTANCE) {
+ // If the cache item is getting updated, wait until it's done updating before
+ // purging it.
+ if (PDU_CACHE_INSTANCE.isUpdating(uri)) {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "updateHeaders: " + uri + " blocked by isUpdating()");
+ }
+ try {
+ PDU_CACHE_INSTANCE.wait();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "updateHeaders: ", e);
+ }
+ }
+ }
+ PDU_CACHE_INSTANCE.purge(uri);
+
+ ContentValues values = new ContentValues(10);
+ byte[] contentType = sendReq.getContentType();
+ if (contentType != null) {
+ values.put(Mms.CONTENT_TYPE, toIsoString(contentType));
+ }
+
+ long date = sendReq.getDate();
+ if (date != -1) {
+ values.put(Mms.DATE, date);
+ }
+
+ int deliveryReport = sendReq.getDeliveryReport();
+ if (deliveryReport != 0) {
+ values.put(Mms.DELIVERY_REPORT, deliveryReport);
+ }
+
+ long expiry = sendReq.getExpiry();
+ if (expiry != -1) {
+ values.put(Mms.EXPIRY, expiry);
+ }
+
+ byte[] msgClass = sendReq.getMessageClass();
+ if (msgClass != null) {
+ values.put(Mms.MESSAGE_CLASS, toIsoString(msgClass));
+ }
+
+ int priority = sendReq.getPriority();
+ if (priority != 0) {
+ values.put(Mms.PRIORITY, priority);
+ }
+
+ int readReport = sendReq.getReadReport();
+ if (readReport != 0) {
+ values.put(Mms.READ_REPORT, readReport);
+ }
+
+ byte[] transId = sendReq.getTransactionId();
+ if (transId != null) {
+ values.put(Mms.TRANSACTION_ID, toIsoString(transId));
+ }
+
+ EncodedStringValue subject = sendReq.getSubject();
+ if (subject != null) {
+ values.put(Mms.SUBJECT, toIsoString(subject.getTextString()));
+ values.put(Mms.SUBJECT_CHARSET, subject.getCharacterSet());
+ } else {
+ values.put(Mms.SUBJECT, "");
+ }
+
+ long messageSize = sendReq.getMessageSize();
+ if (messageSize > 0) {
+ values.put(Mms.MESSAGE_SIZE, messageSize);
+ }
+
+ PduHeaders headers = sendReq.getPduHeaders();
+ HashSet<String> recipients = new HashSet<String>();
+ for (int addrType : ADDRESS_FIELDS) {
+ EncodedStringValue[] array = null;
+ if (addrType == PduHeaders.FROM) {
+ EncodedStringValue v = headers.getEncodedStringValue(addrType);
+ if (v != null) {
+ array = new EncodedStringValue[1];
+ array[0] = v;
+ }
+ } else {
+ array = headers.getEncodedStringValues(addrType);
+ }
+
+ if (array != null) {
+ long msgId = ContentUris.parseId(uri);
+ updateAddress(msgId, addrType, array);
+ if (addrType == PduHeaders.TO) {
+ for (EncodedStringValue v : array) {
+ if (v != null) {
+ recipients.add(v.getString());
+ }
+ }
+ }
+ }
+ }
+ if (!recipients.isEmpty()) {
+ long threadId = Threads.getOrCreateThreadId(mContext, recipients);
+ values.put(Mms.THREAD_ID, threadId);
+ }
+
+ SqliteWrapper.update(mContext, mContentResolver, uri, values, null, null);
+ }
+
+ private void updatePart(Uri uri, PduPart part, HashMap<Uri, InputStream> preOpenedFiles)
+ throws MmsException {
+ ContentValues values = new ContentValues(7);
+
+ int charset = part.getCharset();
+ if (charset != 0 ) {
+ values.put(Part.CHARSET, charset);
+ }
+
+ String contentType = null;
+ if (part.getContentType() != null) {
+ contentType = toIsoString(part.getContentType());
+ values.put(Part.CONTENT_TYPE, contentType);
+ } else {
+ throw new MmsException("MIME type of the part must be set.");
+ }
+
+ if (part.getFilename() != null) {
+ String fileName = new String(part.getFilename());
+ values.put(Part.FILENAME, fileName);
+ }
+
+ if (part.getName() != null) {
+ String name = new String(part.getName());
+ values.put(Part.NAME, name);
+ }
+
+ Object value = null;
+ if (part.getContentDisposition() != null) {
+ value = toIsoString(part.getContentDisposition());
+ values.put(Part.CONTENT_DISPOSITION, (String) value);
+ }
+
+ if (part.getContentId() != null) {
+ value = toIsoString(part.getContentId());
+ values.put(Part.CONTENT_ID, (String) value);
+ }
+
+ if (part.getContentLocation() != null) {
+ value = toIsoString(part.getContentLocation());
+ values.put(Part.CONTENT_LOCATION, (String) value);
+ }
+
+ SqliteWrapper.update(mContext, mContentResolver, uri, values, null, null);
+
+ // Only update the data when:
+ // 1. New binary data supplied or
+ // 2. The Uri of the part is different from the current one.
+ if ((part.getData() != null)
+ || (!uri.equals(part.getDataUri()))) {
+ persistData(part, uri, contentType, preOpenedFiles);
+ }
+ }
+
+ /**
+ * Update all parts of a PDU.
+ *
+ * @param uri The PDU which need to be updated.
+ * @param body New message body of the PDU.
+ * @param preOpenedFiles if not null, a map of preopened InputStreams for the parts.
+ * @throws MmsException Bad URI or updating failed.
+ */
+ @UnsupportedAppUsage
+ public void updateParts(Uri uri, PduBody body, HashMap<Uri, InputStream> preOpenedFiles)
+ throws MmsException {
+ try {
+ PduCacheEntry cacheEntry;
+ synchronized(PDU_CACHE_INSTANCE) {
+ if (PDU_CACHE_INSTANCE.isUpdating(uri)) {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "updateParts: " + uri + " blocked by isUpdating()");
+ }
+ try {
+ PDU_CACHE_INSTANCE.wait();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "updateParts: ", e);
+ }
+ cacheEntry = PDU_CACHE_INSTANCE.get(uri);
+ if (cacheEntry != null) {
+ ((MultimediaMessagePdu) cacheEntry.getPdu()).setBody(body);
+ }
+ }
+ // Tell the cache to indicate to other callers that this item
+ // is currently being updated.
+ PDU_CACHE_INSTANCE.setUpdating(uri, true);
+ }
+
+ ArrayList<PduPart> toBeCreated = new ArrayList<PduPart>();
+ HashMap<Uri, PduPart> toBeUpdated = new HashMap<Uri, PduPart>();
+
+ int partsNum = body.getPartsNum();
+ StringBuilder filter = new StringBuilder().append('(');
+ for (int i = 0; i < partsNum; i++) {
+ PduPart part = body.getPart(i);
+ Uri partUri = part.getDataUri();
+ if ((partUri == null) || TextUtils.isEmpty(partUri.getAuthority())
+ || !partUri.getAuthority().startsWith("mms")) {
+ toBeCreated.add(part);
+ } else {
+ toBeUpdated.put(partUri, part);
+
+ // Don't use 'i > 0' to determine whether we should append
+ // 'AND' since 'i = 0' may be skipped in another branch.
+ if (filter.length() > 1) {
+ filter.append(" AND ");
+ }
+
+ filter.append(Part._ID);
+ filter.append("!=");
+ DatabaseUtils.appendEscapedSQLString(filter, partUri.getLastPathSegment());
+ }
+ }
+ filter.append(')');
+
+ long msgId = ContentUris.parseId(uri);
+
+ // Remove the parts which doesn't exist anymore.
+ SqliteWrapper.delete(mContext, mContentResolver,
+ Uri.parse(Mms.CONTENT_URI + "/" + msgId + "/part"),
+ filter.length() > 2 ? filter.toString() : null, null);
+
+ // Create new parts which didn't exist before.
+ for (PduPart part : toBeCreated) {
+ persistPart(part, msgId, preOpenedFiles);
+ }
+
+ // Update the modified parts.
+ for (Map.Entry<Uri, PduPart> e : toBeUpdated.entrySet()) {
+ updatePart(e.getKey(), e.getValue(), preOpenedFiles);
+ }
+ } finally {
+ synchronized(PDU_CACHE_INSTANCE) {
+ PDU_CACHE_INSTANCE.setUpdating(uri, false);
+ PDU_CACHE_INSTANCE.notifyAll();
+ }
+ }
+ }
+
+ /**
+ * Persist a PDU object to specific location in the storage.
+ *
+ * @param pdu The PDU object to be stored.
+ * @param uri Where to store the given PDU object.
+ * @param createThreadId if true, this function may create a thread id for the recipients
+ * @param groupMmsEnabled if true, all of the recipients addressed in the PDU will be used
+ * to create the associated thread. When false, only the sender will be used in finding or
+ * creating the appropriate thread or conversation.
+ * @param preOpenedFiles if not null, a map of preopened InputStreams for the parts.
+ * @return A Uri which can be used to access the stored PDU.
+ */
+
+ @UnsupportedAppUsage
+ public Uri persist(GenericPdu pdu, Uri uri, boolean createThreadId, boolean groupMmsEnabled,
+ HashMap<Uri, InputStream> preOpenedFiles)
+ throws MmsException {
+ if (uri == null) {
+ throw new MmsException("Uri may not be null.");
+ }
+ long msgId = -1;
+ try {
+ msgId = ContentUris.parseId(uri);
+ } catch (NumberFormatException e) {
+ // the uri ends with "inbox" or something else like that
+ }
+ boolean existingUri = msgId != -1;
+
+ if (!existingUri && MESSAGE_BOX_MAP.get(uri) == null) {
+ throw new MmsException(
+ "Bad destination, must be one of "
+ + "content://mms/inbox, content://mms/sent, "
+ + "content://mms/drafts, content://mms/outbox, "
+ + "content://mms/temp.");
+ }
+ synchronized(PDU_CACHE_INSTANCE) {
+ // If the cache item is getting updated, wait until it's done updating before
+ // purging it.
+ if (PDU_CACHE_INSTANCE.isUpdating(uri)) {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "persist: " + uri + " blocked by isUpdating()");
+ }
+ try {
+ PDU_CACHE_INSTANCE.wait();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "persist1: ", e);
+ }
+ }
+ }
+ PDU_CACHE_INSTANCE.purge(uri);
+
+ PduHeaders header = pdu.getPduHeaders();
+ PduBody body = null;
+ ContentValues values = new ContentValues();
+ Set<Entry<Integer, String>> set;
+
+ set = ENCODED_STRING_COLUMN_NAME_MAP.entrySet();
+ for (Entry<Integer, String> e : set) {
+ int field = e.getKey();
+ EncodedStringValue encodedString = header.getEncodedStringValue(field);
+ if (encodedString != null) {
+ String charsetColumn = CHARSET_COLUMN_NAME_MAP.get(field);
+ values.put(e.getValue(), toIsoString(encodedString.getTextString()));
+ values.put(charsetColumn, encodedString.getCharacterSet());
+ }
+ }
+
+ set = TEXT_STRING_COLUMN_NAME_MAP.entrySet();
+ for (Entry<Integer, String> e : set){
+ byte[] text = header.getTextString(e.getKey());
+ if (text != null) {
+ values.put(e.getValue(), toIsoString(text));
+ }
+ }
+
+ set = OCTET_COLUMN_NAME_MAP.entrySet();
+ for (Entry<Integer, String> e : set){
+ int b = header.getOctet(e.getKey());
+ if (b != 0) {
+ values.put(e.getValue(), b);
+ }
+ }
+
+ set = LONG_COLUMN_NAME_MAP.entrySet();
+ for (Entry<Integer, String> e : set){
+ long l = header.getLongInteger(e.getKey());
+ if (l != -1L) {
+ values.put(e.getValue(), l);
+ }
+ }
+
+ HashMap<Integer, EncodedStringValue[]> addressMap =
+ new HashMap<Integer, EncodedStringValue[]>(ADDRESS_FIELDS.length);
+ // Save address information.
+ for (int addrType : ADDRESS_FIELDS) {
+ EncodedStringValue[] array = null;
+ if (addrType == PduHeaders.FROM) {
+ EncodedStringValue v = header.getEncodedStringValue(addrType);
+ if (v != null) {
+ array = new EncodedStringValue[1];
+ array[0] = v;
+ }
+ } else {
+ array = header.getEncodedStringValues(addrType);
+ }
+ addressMap.put(addrType, array);
+ }
+
+ HashSet<String> recipients = new HashSet<String>();
+ int msgType = pdu.getMessageType();
+ // Here we only allocate thread ID for M-Notification.ind,
+ // M-Retrieve.conf and M-Send.req.
+ // Some of other PDU types may be allocated a thread ID outside
+ // this scope.
+ if ((msgType == PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND)
+ || (msgType == PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF)
+ || (msgType == PduHeaders.MESSAGE_TYPE_SEND_REQ)) {
+ switch (msgType) {
+ case PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND:
+ case PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF:
+ loadRecipients(PduHeaders.FROM, recipients, addressMap, false);
+
+ // For received messages when group MMS is enabled, we want to associate this
+ // message with the thread composed of all the recipients -- all but our own
+ // number, that is. This includes the person who sent the
+ // message or the FROM field (above) in addition to the other people the message
+ // was addressed to or the TO field. Our own number is in that TO field and
+ // we have to ignore it in loadRecipients.
+ if (groupMmsEnabled) {
+ loadRecipients(PduHeaders.TO, recipients, addressMap, true);
+
+ // Also load any numbers in the CC field to address group messaging
+ // compatibility issues with devices that place numbers in this field
+ // for group messages.
+ loadRecipients(PduHeaders.CC, recipients, addressMap, true);
+ }
+ break;
+ case PduHeaders.MESSAGE_TYPE_SEND_REQ:
+ loadRecipients(PduHeaders.TO, recipients, addressMap, false);
+ break;
+ }
+ long threadId = 0;
+ if (createThreadId && !recipients.isEmpty()) {
+ // Given all the recipients associated with this message, find (or create) the
+ // correct thread.
+ threadId = Threads.getOrCreateThreadId(mContext, recipients);
+ }
+ values.put(Mms.THREAD_ID, threadId);
+ }
+
+ // Save parts first to avoid inconsistent message is loaded
+ // while saving the parts.
+ long dummyId = System.currentTimeMillis(); // Dummy ID of the msg.
+
+ // Figure out if this PDU is a text-only message
+ boolean textOnly = true;
+
+ // Sum up the total message size
+ int messageSize = 0;
+
+ // Get body if the PDU is a RetrieveConf or SendReq.
+ if (pdu instanceof MultimediaMessagePdu) {
+ body = ((MultimediaMessagePdu) pdu).getBody();
+ // Start saving parts if necessary.
+ if (body != null) {
+ int partsNum = body.getPartsNum();
+ if (partsNum > 2) {
+ // For a text-only message there will be two parts: 1-the SMIL, 2-the text.
+ // Down a few lines below we're checking to make sure we've only got SMIL or
+ // text. We also have to check then we don't have more than two parts.
+ // Otherwise, a slideshow with two text slides would be marked as textOnly.
+ textOnly = false;
+ }
+ for (int i = 0; i < partsNum; i++) {
+ PduPart part = body.getPart(i);
+ messageSize += part.getDataLength();
+ persistPart(part, dummyId, preOpenedFiles);
+
+ // If we've got anything besides text/plain or SMIL part, then we've got
+ // an mms message with some other type of attachment.
+ String contentType = getPartContentType(part);
+ if (contentType != null && !ContentType.APP_SMIL.equals(contentType)
+ && !ContentType.TEXT_PLAIN.equals(contentType)) {
+ textOnly = false;
+ }
+ }
+ }
+ }
+ // Record whether this mms message is a simple plain text or not. This is a hint for the
+ // UI.
+ values.put(Mms.TEXT_ONLY, textOnly ? 1 : 0);
+ // The message-size might already have been inserted when parsing the
+ // PDU header. If not, then we insert the message size as well.
+ if (values.getAsInteger(Mms.MESSAGE_SIZE) == null) {
+ values.put(Mms.MESSAGE_SIZE, messageSize);
+ }
+
+ Uri res = null;
+ if (existingUri) {
+ res = uri;
+ SqliteWrapper.update(mContext, mContentResolver, res, values, null, null);
+ } else {
+ res = SqliteWrapper.insert(mContext, mContentResolver, uri, values);
+ if (res == null) {
+ throw new MmsException("persist() failed: return null.");
+ }
+ // Get the real ID of the PDU and update all parts which were
+ // saved with the dummy ID.
+ msgId = ContentUris.parseId(res);
+ }
+
+ values = new ContentValues(1);
+ values.put(Part.MSG_ID, msgId);
+ SqliteWrapper.update(mContext, mContentResolver,
+ Uri.parse("content://mms/" + dummyId + "/part"),
+ values, null, null);
+ // We should return the longest URI of the persisted PDU, for
+ // example, if input URI is "content://mms/inbox" and the _ID of
+ // persisted PDU is '8', we should return "content://mms/inbox/8"
+ // instead of "content://mms/8".
+ // FIXME: Should the MmsProvider be responsible for this???
+ if (!existingUri) {
+ res = Uri.parse(uri + "/" + msgId);
+ }
+
+ // Save address information.
+ for (int addrType : ADDRESS_FIELDS) {
+ EncodedStringValue[] array = addressMap.get(addrType);
+ if (array != null) {
+ persistAddress(msgId, addrType, array);
+ }
+ }
+
+ return res;
+ }
+
+ /**
+ * For a given address type, extract the recipients from the headers.
+ *
+ * @param addressType can be PduHeaders.FROM, PduHeaders.TO or PduHeaders.CC
+ * @param recipients a HashSet that is loaded with the recipients from the FROM, TO or CC headers
+ * @param addressMap a HashMap of the addresses from the ADDRESS_FIELDS header
+ * @param excludeMyNumber if true, the number of this phone will be excluded from recipients
+ */
+ @UnsupportedAppUsage
+ private void loadRecipients(int addressType, HashSet<String> recipients,
+ HashMap<Integer, EncodedStringValue[]> addressMap, boolean excludeMyNumber) {
+ EncodedStringValue[] array = addressMap.get(addressType);
+ if (array == null) {
+ return;
+ }
+ // If the TO recipients is only a single address, then we can skip loadRecipients when
+ // we're excluding our own number because we know that address is our own.
+ if (excludeMyNumber && array.length == 1) {
+ return;
+ }
+ final SubscriptionManager subscriptionManager = SubscriptionManager.from(mContext);
+ final Set<String> myPhoneNumbers = new HashSet<String>();
+ if (excludeMyNumber) {
+ // Build a list of my phone numbers from the various sims.
+ for (int subid : subscriptionManager.getActiveSubscriptionIdList()) {
+ final String myNumber = mTelephonyManager.getLine1Number(subid);
+ if (myNumber != null) {
+ myPhoneNumbers.add(myNumber);
+ }
+ }
+ }
+
+ for (EncodedStringValue v : array) {
+ if (v != null) {
+ final String number = v.getString();
+ if (excludeMyNumber) {
+ for (final String myNumber : myPhoneNumbers) {
+ if (!PhoneNumberUtils.compare(number, myNumber)
+ && !recipients.contains(number)) {
+ // Only add numbers which aren't my own number.
+ recipients.add(number);
+ break;
+ }
+ }
+ } else if (!recipients.contains(number)){
+ recipients.add(number);
+ }
+ }
+ }
+ }
+
+ /**
+ * Move a PDU object from one location to another.
+ *
+ * @param from Specify the PDU object to be moved.
+ * @param to The destination location, should be one of the following:
+ * "content://mms/inbox", "content://mms/sent",
+ * "content://mms/drafts", "content://mms/outbox",
+ * "content://mms/trash".
+ * @return New Uri of the moved PDU.
+ * @throws MmsException Error occurred while moving the message.
+ */
+ @UnsupportedAppUsage
+ public Uri move(Uri from, Uri to) throws MmsException {
+ // Check whether the 'msgId' has been assigned a valid value.
+ long msgId = ContentUris.parseId(from);
+ if (msgId == -1L) {
+ throw new MmsException("Error! ID of the message: -1.");
+ }
+
+ // Get corresponding int value of destination box.
+ Integer msgBox = MESSAGE_BOX_MAP.get(to);
+ if (msgBox == null) {
+ throw new MmsException(
+ "Bad destination, must be one of "
+ + "content://mms/inbox, content://mms/sent, "
+ + "content://mms/drafts, content://mms/outbox, "
+ + "content://mms/temp.");
+ }
+
+ ContentValues values = new ContentValues(1);
+ values.put(Mms.MESSAGE_BOX, msgBox);
+ SqliteWrapper.update(mContext, mContentResolver, from, values, null, null);
+ return ContentUris.withAppendedId(to, msgId);
+ }
+
+ /**
+ * Wrap a byte[] into a String.
+ */
+ @UnsupportedAppUsage
+ public static String toIsoString(byte[] bytes) {
+ try {
+ return new String(bytes, CharacterSets.MIMENAME_ISO_8859_1);
+ } catch (UnsupportedEncodingException e) {
+ // Impossible to reach here!
+ Log.e(TAG, "ISO_8859_1 must be supported!", e);
+ return "";
+ }
+ }
+
+ /**
+ * Unpack a given String into a byte[].
+ */
+ @UnsupportedAppUsage
+ public static byte[] getBytes(String data) {
+ try {
+ return data.getBytes(CharacterSets.MIMENAME_ISO_8859_1);
+ } catch (UnsupportedEncodingException e) {
+ // Impossible to reach here!
+ Log.e(TAG, "ISO_8859_1 must be supported!", e);
+ return new byte[0];
+ }
+ }
+
+ /**
+ * Remove all objects in the temporary path.
+ */
+ public void release() {
+ Uri uri = Uri.parse(TEMPORARY_DRM_OBJECT_URI);
+ SqliteWrapper.delete(mContext, mContentResolver, uri, null, null);
+ }
+
+ /**
+ * Find all messages to be sent or downloaded before certain time.
+ */
+ @UnsupportedAppUsage
+ public Cursor getPendingMessages(long dueTime) {
+ Uri.Builder uriBuilder = PendingMessages.CONTENT_URI.buildUpon();
+ uriBuilder.appendQueryParameter("protocol", "mms");
+
+ String selection = PendingMessages.ERROR_TYPE + " < ?"
+ + " AND " + PendingMessages.DUE_TIME + " <= ?";
+
+ String[] selectionArgs = new String[] {
+ String.valueOf(MmsSms.ERR_TYPE_GENERIC_PERMANENT),
+ String.valueOf(dueTime)
+ };
+
+ return SqliteWrapper.query(mContext, mContentResolver,
+ uriBuilder.build(), null, selection, selectionArgs,
+ PendingMessages.DUE_TIME);
+ }
+}
diff --git a/telephony/java/com/google/android/mms/pdu/QuotedPrintable.java b/telephony/java/com/google/android/mms/pdu/QuotedPrintable.java
new file mode 100644
index 000000000000..9d6535c72e90
--- /dev/null
+++ b/telephony/java/com/google/android/mms/pdu/QuotedPrintable.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import java.io.ByteArrayOutputStream;
+
+public class QuotedPrintable {
+ private static byte ESCAPE_CHAR = '=';
+
+ /**
+ * Decodes an array quoted-printable characters into an array of original bytes.
+ * Escaped characters are converted back to their original representation.
+ *
+ * <p>
+ * This function implements a subset of
+ * quoted-printable encoding specification (rule #1 and rule #2)
+ * as defined in RFC 1521.
+ * </p>
+ *
+ * @param bytes array of quoted-printable characters
+ * @return array of original bytes,
+ * null if quoted-printable decoding is unsuccessful.
+ */
+ @UnsupportedAppUsage
+ public static final byte[] decodeQuotedPrintable(byte[] bytes) {
+ if (bytes == null) {
+ return null;
+ }
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+ for (int i = 0; i < bytes.length; i++) {
+ int b = bytes[i];
+ if (b == ESCAPE_CHAR) {
+ try {
+ if('\r' == (char)bytes[i + 1] &&
+ '\n' == (char)bytes[i + 2]) {
+ i += 2;
+ continue;
+ }
+ int u = Character.digit((char) bytes[++i], 16);
+ int l = Character.digit((char) bytes[++i], 16);
+ if (u == -1 || l == -1) {
+ return null;
+ }
+ buffer.write((char) ((u << 4) + l));
+ } catch (ArrayIndexOutOfBoundsException e) {
+ return null;
+ }
+ } else {
+ buffer.write(b);
+ }
+ }
+ return buffer.toByteArray();
+ }
+}
diff --git a/telephony/java/com/google/android/mms/pdu/ReadOrigInd.java b/telephony/java/com/google/android/mms/pdu/ReadOrigInd.java
new file mode 100644
index 000000000000..e38c62dde622
--- /dev/null
+++ b/telephony/java/com/google/android/mms/pdu/ReadOrigInd.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import com.google.android.mms.InvalidHeaderValueException;
+
+public class ReadOrigInd extends GenericPdu {
+ /**
+ * Empty constructor.
+ * Since the Pdu corresponding to this class is constructed
+ * by the Proxy-Relay server, this class is only instantiated
+ * by the Pdu Parser.
+ *
+ * @throws InvalidHeaderValueException if error occurs.
+ */
+ public ReadOrigInd() throws InvalidHeaderValueException {
+ super();
+ setMessageType(PduHeaders.MESSAGE_TYPE_READ_ORIG_IND);
+ }
+
+ /**
+ * Constructor with given headers.
+ *
+ * @param headers Headers for this PDU.
+ */
+ @UnsupportedAppUsage
+ ReadOrigInd(PduHeaders headers) {
+ super(headers);
+ }
+
+ /**
+ * Get Date value.
+ *
+ * @return the value
+ */
+ public long getDate() {
+ return mPduHeaders.getLongInteger(PduHeaders.DATE);
+ }
+
+ /**
+ * Set Date value.
+ *
+ * @param value the value
+ */
+ public void setDate(long value) {
+ mPduHeaders.setLongInteger(value, PduHeaders.DATE);
+ }
+
+ /**
+ * Get From value.
+ * From-value = Value-length
+ * (Address-present-token Encoded-string-value | Insert-address-token)
+ *
+ * @return the value
+ */
+ public EncodedStringValue getFrom() {
+ return mPduHeaders.getEncodedStringValue(PduHeaders.FROM);
+ }
+
+ /**
+ * Set From value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ public void setFrom(EncodedStringValue value) {
+ mPduHeaders.setEncodedStringValue(value, PduHeaders.FROM);
+ }
+
+ /**
+ * Get Message-ID value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getMessageId() {
+ return mPduHeaders.getTextString(PduHeaders.MESSAGE_ID);
+ }
+
+ /**
+ * Set Message-ID value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ public void setMessageId(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.MESSAGE_ID);
+ }
+
+ /**
+ * Get X-MMS-Read-status value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public int getReadStatus() {
+ return mPduHeaders.getOctet(PduHeaders.READ_STATUS);
+ }
+
+ /**
+ * Set X-MMS-Read-status value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ */
+ public void setReadStatus(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.READ_STATUS);
+ }
+
+ /**
+ * Get To value.
+ *
+ * @return the value
+ */
+ public EncodedStringValue[] getTo() {
+ return mPduHeaders.getEncodedStringValues(PduHeaders.TO);
+ }
+
+ /**
+ * Set To value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ public void setTo(EncodedStringValue[] value) {
+ mPduHeaders.setEncodedStringValues(value, PduHeaders.TO);
+ }
+
+ /*
+ * Optional, not supported header fields:
+ *
+ * public byte[] getApplicId() {return null;}
+ * public void setApplicId(byte[] value) {}
+ *
+ * public byte[] getAuxApplicId() {return null;}
+ * public void getAuxApplicId(byte[] value) {}
+ *
+ * public byte[] getReplyApplicId() {return 0x00;}
+ * public void setReplyApplicId(byte[] value) {}
+ */
+}
diff --git a/telephony/java/com/google/android/mms/pdu/ReadRecInd.java b/telephony/java/com/google/android/mms/pdu/ReadRecInd.java
new file mode 100644
index 000000000000..9696bc259d00
--- /dev/null
+++ b/telephony/java/com/google/android/mms/pdu/ReadRecInd.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import com.google.android.mms.InvalidHeaderValueException;
+
+public class ReadRecInd extends GenericPdu {
+ /**
+ * Constructor, used when composing a M-ReadRec.ind pdu.
+ *
+ * @param from the from value
+ * @param messageId the message ID value
+ * @param mmsVersion current viersion of mms
+ * @param readStatus the read status value
+ * @param to the to value
+ * @throws InvalidHeaderValueException if parameters are invalid.
+ * NullPointerException if messageId or to is null.
+ */
+ @UnsupportedAppUsage
+ public ReadRecInd(EncodedStringValue from,
+ byte[] messageId,
+ int mmsVersion,
+ int readStatus,
+ EncodedStringValue[] to) throws InvalidHeaderValueException {
+ super();
+ setMessageType(PduHeaders.MESSAGE_TYPE_READ_REC_IND);
+ setFrom(from);
+ setMessageId(messageId);
+ setMmsVersion(mmsVersion);
+ setTo(to);
+ setReadStatus(readStatus);
+ }
+
+ /**
+ * Constructor with given headers.
+ *
+ * @param headers Headers for this PDU.
+ */
+ @UnsupportedAppUsage
+ ReadRecInd(PduHeaders headers) {
+ super(headers);
+ }
+
+ /**
+ * Get Date value.
+ *
+ * @return the value
+ */
+ public long getDate() {
+ return mPduHeaders.getLongInteger(PduHeaders.DATE);
+ }
+
+ /**
+ * Set Date value.
+ *
+ * @param value the value
+ */
+ @UnsupportedAppUsage
+ public void setDate(long value) {
+ mPduHeaders.setLongInteger(value, PduHeaders.DATE);
+ }
+
+ /**
+ * Get Message-ID value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getMessageId() {
+ return mPduHeaders.getTextString(PduHeaders.MESSAGE_ID);
+ }
+
+ /**
+ * Set Message-ID value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ public void setMessageId(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.MESSAGE_ID);
+ }
+
+ /**
+ * Get To value.
+ *
+ * @return the value
+ */
+ public EncodedStringValue[] getTo() {
+ return mPduHeaders.getEncodedStringValues(PduHeaders.TO);
+ }
+
+ /**
+ * Set To value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ public void setTo(EncodedStringValue[] value) {
+ mPduHeaders.setEncodedStringValues(value, PduHeaders.TO);
+ }
+
+ /**
+ * Get X-MMS-Read-status value.
+ *
+ * @return the value
+ */
+ public int getReadStatus() {
+ return mPduHeaders.getOctet(PduHeaders.READ_STATUS);
+ }
+
+ /**
+ * Set X-MMS-Read-status value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ */
+ public void setReadStatus(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.READ_STATUS);
+ }
+
+ /*
+ * Optional, not supported header fields:
+ *
+ * public byte[] getApplicId() {return null;}
+ * public void setApplicId(byte[] value) {}
+ *
+ * public byte[] getAuxApplicId() {return null;}
+ * public void getAuxApplicId(byte[] value) {}
+ *
+ * public byte[] getReplyApplicId() {return 0x00;}
+ * public void setReplyApplicId(byte[] value) {}
+ */
+}
diff --git a/telephony/java/com/google/android/mms/pdu/RetrieveConf.java b/telephony/java/com/google/android/mms/pdu/RetrieveConf.java
new file mode 100644
index 000000000000..03755af4189c
--- /dev/null
+++ b/telephony/java/com/google/android/mms/pdu/RetrieveConf.java
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import com.google.android.mms.InvalidHeaderValueException;
+
+/**
+ * M-Retrieve.conf Pdu.
+ */
+public class RetrieveConf extends MultimediaMessagePdu {
+ /**
+ * Empty constructor.
+ * Since the Pdu corresponding to this class is constructed
+ * by the Proxy-Relay server, this class is only instantiated
+ * by the Pdu Parser.
+ *
+ * @throws InvalidHeaderValueException if error occurs.
+ */
+ @UnsupportedAppUsage
+ public RetrieveConf() throws InvalidHeaderValueException {
+ super();
+ setMessageType(PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF);
+ }
+
+ /**
+ * Constructor with given headers.
+ *
+ * @param headers Headers for this PDU.
+ */
+ RetrieveConf(PduHeaders headers) {
+ super(headers);
+ }
+
+ /**
+ * Constructor with given headers and body
+ *
+ * @param headers Headers for this PDU.
+ * @param body Body of this PDu.
+ */
+ @UnsupportedAppUsage
+ RetrieveConf(PduHeaders headers, PduBody body) {
+ super(headers, body);
+ }
+
+ /**
+ * Get CC value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public EncodedStringValue[] getCc() {
+ return mPduHeaders.getEncodedStringValues(PduHeaders.CC);
+ }
+
+ /**
+ * Add a "CC" value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void addCc(EncodedStringValue value) {
+ mPduHeaders.appendEncodedStringValue(value, PduHeaders.CC);
+ }
+
+ /**
+ * Get Content-type value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getContentType() {
+ return mPduHeaders.getTextString(PduHeaders.CONTENT_TYPE);
+ }
+
+ /**
+ * Set Content-type value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setContentType(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.CONTENT_TYPE);
+ }
+
+ /**
+ * Get X-Mms-Delivery-Report value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public int getDeliveryReport() {
+ return mPduHeaders.getOctet(PduHeaders.DELIVERY_REPORT);
+ }
+
+ /**
+ * Set X-Mms-Delivery-Report value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ */
+ @UnsupportedAppUsage
+ public void setDeliveryReport(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.DELIVERY_REPORT);
+ }
+
+ /**
+ * Get From value.
+ * From-value = Value-length
+ * (Address-present-token Encoded-string-value | Insert-address-token)
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public EncodedStringValue getFrom() {
+ return mPduHeaders.getEncodedStringValue(PduHeaders.FROM);
+ }
+
+ /**
+ * Set From value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setFrom(EncodedStringValue value) {
+ mPduHeaders.setEncodedStringValue(value, PduHeaders.FROM);
+ }
+
+ /**
+ * Get X-Mms-Message-Class value.
+ * Message-class-value = Class-identifier | Token-text
+ * Class-identifier = Personal | Advertisement | Informational | Auto
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getMessageClass() {
+ return mPduHeaders.getTextString(PduHeaders.MESSAGE_CLASS);
+ }
+
+ /**
+ * Set X-Mms-Message-Class value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setMessageClass(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.MESSAGE_CLASS);
+ }
+
+ /**
+ * Get Message-ID value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getMessageId() {
+ return mPduHeaders.getTextString(PduHeaders.MESSAGE_ID);
+ }
+
+ /**
+ * Set Message-ID value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setMessageId(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.MESSAGE_ID);
+ }
+
+ /**
+ * Get X-Mms-Read-Report value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public int getReadReport() {
+ return mPduHeaders.getOctet(PduHeaders.READ_REPORT);
+ }
+
+ /**
+ * Set X-Mms-Read-Report value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ */
+ @UnsupportedAppUsage
+ public void setReadReport(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.READ_REPORT);
+ }
+
+ /**
+ * Get X-Mms-Retrieve-Status value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public int getRetrieveStatus() {
+ return mPduHeaders.getOctet(PduHeaders.RETRIEVE_STATUS);
+ }
+
+ /**
+ * Set X-Mms-Retrieve-Status value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ */
+ @UnsupportedAppUsage
+ public void setRetrieveStatus(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.RETRIEVE_STATUS);
+ }
+
+ /**
+ * Get X-Mms-Retrieve-Text value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public EncodedStringValue getRetrieveText() {
+ return mPduHeaders.getEncodedStringValue(PduHeaders.RETRIEVE_TEXT);
+ }
+
+ /**
+ * Set X-Mms-Retrieve-Text value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setRetrieveText(EncodedStringValue value) {
+ mPduHeaders.setEncodedStringValue(value, PduHeaders.RETRIEVE_TEXT);
+ }
+
+ /**
+ * Get X-Mms-Transaction-Id.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getTransactionId() {
+ return mPduHeaders.getTextString(PduHeaders.TRANSACTION_ID);
+ }
+
+ /**
+ * Set X-Mms-Transaction-Id.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setTransactionId(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.TRANSACTION_ID);
+ }
+
+ /*
+ * Optional, not supported header fields:
+ *
+ * public byte[] getApplicId() {return null;}
+ * public void setApplicId(byte[] value) {}
+ *
+ * public byte[] getAuxApplicId() {return null;}
+ * public void getAuxApplicId(byte[] value) {}
+ *
+ * public byte getContentClass() {return 0x00;}
+ * public void setApplicId(byte value) {}
+ *
+ * public byte getDrmContent() {return 0x00;}
+ * public void setDrmContent(byte value) {}
+ *
+ * public byte getDistributionIndicator() {return 0x00;}
+ * public void setDistributionIndicator(byte value) {}
+ *
+ * public PreviouslySentByValue getPreviouslySentBy() {return null;}
+ * public void setPreviouslySentBy(PreviouslySentByValue value) {}
+ *
+ * public PreviouslySentDateValue getPreviouslySentDate() {}
+ * public void setPreviouslySentDate(PreviouslySentDateValue value) {}
+ *
+ * public MmFlagsValue getMmFlags() {return null;}
+ * public void setMmFlags(MmFlagsValue value) {}
+ *
+ * public MmStateValue getMmState() {return null;}
+ * public void getMmState(MmStateValue value) {}
+ *
+ * public byte[] getReplaceId() {return 0x00;}
+ * public void setReplaceId(byte[] value) {}
+ *
+ * public byte[] getReplyApplicId() {return 0x00;}
+ * public void setReplyApplicId(byte[] value) {}
+ *
+ * public byte getReplyCharging() {return 0x00;}
+ * public void setReplyCharging(byte value) {}
+ *
+ * public byte getReplyChargingDeadline() {return 0x00;}
+ * public void setReplyChargingDeadline(byte value) {}
+ *
+ * public byte[] getReplyChargingId() {return 0x00;}
+ * public void setReplyChargingId(byte[] value) {}
+ *
+ * public long getReplyChargingSize() {return 0;}
+ * public void setReplyChargingSize(long value) {}
+ */
+}
diff --git a/telephony/java/com/google/android/mms/pdu/SendConf.java b/telephony/java/com/google/android/mms/pdu/SendConf.java
new file mode 100644
index 000000000000..b85982791ada
--- /dev/null
+++ b/telephony/java/com/google/android/mms/pdu/SendConf.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import com.google.android.mms.InvalidHeaderValueException;
+
+public class SendConf extends GenericPdu {
+ /**
+ * Empty constructor.
+ * Since the Pdu corresponding to this class is constructed
+ * by the Proxy-Relay server, this class is only instantiated
+ * by the Pdu Parser.
+ *
+ * @throws InvalidHeaderValueException if error occurs.
+ */
+ @UnsupportedAppUsage
+ public SendConf() throws InvalidHeaderValueException {
+ super();
+ setMessageType(PduHeaders.MESSAGE_TYPE_SEND_CONF);
+ }
+
+ /**
+ * Constructor with given headers.
+ *
+ * @param headers Headers for this PDU.
+ */
+ @UnsupportedAppUsage
+ SendConf(PduHeaders headers) {
+ super(headers);
+ }
+
+ /**
+ * Get Message-ID value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getMessageId() {
+ return mPduHeaders.getTextString(PduHeaders.MESSAGE_ID);
+ }
+
+ /**
+ * Set Message-ID value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ public void setMessageId(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.MESSAGE_ID);
+ }
+
+ /**
+ * Get X-Mms-Response-Status.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public int getResponseStatus() {
+ return mPduHeaders.getOctet(PduHeaders.RESPONSE_STATUS);
+ }
+
+ /**
+ * Set X-Mms-Response-Status.
+ *
+ * @param value the values
+ * @throws InvalidHeaderValueException if the value is invalid.
+ */
+ public void setResponseStatus(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.RESPONSE_STATUS);
+ }
+
+ /**
+ * Get X-Mms-Transaction-Id field value.
+ *
+ * @return the X-Mms-Report-Allowed value
+ */
+ @UnsupportedAppUsage
+ public byte[] getTransactionId() {
+ return mPduHeaders.getTextString(PduHeaders.TRANSACTION_ID);
+ }
+
+ /**
+ * Set X-Mms-Transaction-Id field value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ public void setTransactionId(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.TRANSACTION_ID);
+ }
+
+ /*
+ * Optional, not supported header fields:
+ *
+ * public byte[] getContentLocation() {return null;}
+ * public void setContentLocation(byte[] value) {}
+ *
+ * public EncodedStringValue getResponseText() {return null;}
+ * public void setResponseText(EncodedStringValue value) {}
+ *
+ * public byte getStoreStatus() {return 0x00;}
+ * public void setStoreStatus(byte value) {}
+ *
+ * public byte[] getStoreStatusText() {return null;}
+ * public void setStoreStatusText(byte[] value) {}
+ */
+}
diff --git a/telephony/java/com/google/android/mms/pdu/SendReq.java b/telephony/java/com/google/android/mms/pdu/SendReq.java
new file mode 100644
index 000000000000..c1b7f934c0f7
--- /dev/null
+++ b/telephony/java/com/google/android/mms/pdu/SendReq.java
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2007-2008 Esmertec AG.
+ * Copyright (C) 2007-2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.pdu;
+
+import android.util.Log;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import com.google.android.mms.InvalidHeaderValueException;
+
+public class SendReq extends MultimediaMessagePdu {
+ private static final String TAG = "SendReq";
+
+ @UnsupportedAppUsage
+ public SendReq() {
+ super();
+
+ try {
+ setMessageType(PduHeaders.MESSAGE_TYPE_SEND_REQ);
+ setMmsVersion(PduHeaders.CURRENT_MMS_VERSION);
+ // FIXME: Content-type must be decided according to whether
+ // SMIL part present.
+ setContentType("application/vnd.wap.multipart.related".getBytes());
+ setFrom(new EncodedStringValue(PduHeaders.FROM_INSERT_ADDRESS_TOKEN_STR.getBytes()));
+ setTransactionId(generateTransactionId());
+ } catch (InvalidHeaderValueException e) {
+ // Impossible to reach here since all headers we set above are valid.
+ Log.e(TAG, "Unexpected InvalidHeaderValueException.", e);
+ throw new RuntimeException(e);
+ }
+ }
+
+ private byte[] generateTransactionId() {
+ String transactionId = "T" + Long.toHexString(System.currentTimeMillis());
+ return transactionId.getBytes();
+ }
+
+ /**
+ * Constructor, used when composing a M-Send.req pdu.
+ *
+ * @param contentType the content type value
+ * @param from the from value
+ * @param mmsVersion current viersion of mms
+ * @param transactionId the transaction-id value
+ * @throws InvalidHeaderValueException if parameters are invalid.
+ * NullPointerException if contentType, form or transactionId is null.
+ */
+ public SendReq(byte[] contentType,
+ EncodedStringValue from,
+ int mmsVersion,
+ byte[] transactionId) throws InvalidHeaderValueException {
+ super();
+ setMessageType(PduHeaders.MESSAGE_TYPE_SEND_REQ);
+ setContentType(contentType);
+ setFrom(from);
+ setMmsVersion(mmsVersion);
+ setTransactionId(transactionId);
+ }
+
+ /**
+ * Constructor with given headers.
+ *
+ * @param headers Headers for this PDU.
+ */
+ SendReq(PduHeaders headers) {
+ super(headers);
+ }
+
+ /**
+ * Constructor with given headers and body
+ *
+ * @param headers Headers for this PDU.
+ * @param body Body of this PDu.
+ */
+ @UnsupportedAppUsage
+ SendReq(PduHeaders headers, PduBody body) {
+ super(headers, body);
+ }
+
+ /**
+ * Get Bcc value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public EncodedStringValue[] getBcc() {
+ return mPduHeaders.getEncodedStringValues(PduHeaders.BCC);
+ }
+
+ /**
+ * Add a "BCC" value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void addBcc(EncodedStringValue value) {
+ mPduHeaders.appendEncodedStringValue(value, PduHeaders.BCC);
+ }
+
+ /**
+ * Set "BCC" value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setBcc(EncodedStringValue[] value) {
+ mPduHeaders.setEncodedStringValues(value, PduHeaders.BCC);
+ }
+
+ /**
+ * Get CC value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public EncodedStringValue[] getCc() {
+ return mPduHeaders.getEncodedStringValues(PduHeaders.CC);
+ }
+
+ /**
+ * Add a "CC" value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void addCc(EncodedStringValue value) {
+ mPduHeaders.appendEncodedStringValue(value, PduHeaders.CC);
+ }
+
+ /**
+ * Set "CC" value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setCc(EncodedStringValue[] value) {
+ mPduHeaders.setEncodedStringValues(value, PduHeaders.CC);
+ }
+
+ /**
+ * Get Content-type value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getContentType() {
+ return mPduHeaders.getTextString(PduHeaders.CONTENT_TYPE);
+ }
+
+ /**
+ * Set Content-type value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setContentType(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.CONTENT_TYPE);
+ }
+
+ /**
+ * Get X-Mms-Delivery-Report value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public int getDeliveryReport() {
+ return mPduHeaders.getOctet(PduHeaders.DELIVERY_REPORT);
+ }
+
+ /**
+ * Set X-Mms-Delivery-Report value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ */
+ @UnsupportedAppUsage
+ public void setDeliveryReport(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.DELIVERY_REPORT);
+ }
+
+ /**
+ * Get X-Mms-Expiry value.
+ *
+ * Expiry-value = Value-length
+ * (Absolute-token Date-value | Relative-token Delta-seconds-value)
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public long getExpiry() {
+ return mPduHeaders.getLongInteger(PduHeaders.EXPIRY);
+ }
+
+ /**
+ * Set X-Mms-Expiry value.
+ *
+ * @param value the value
+ */
+ @UnsupportedAppUsage
+ public void setExpiry(long value) {
+ mPduHeaders.setLongInteger(value, PduHeaders.EXPIRY);
+ }
+
+ /**
+ * Get X-Mms-MessageSize value.
+ *
+ * Expiry-value = size of message
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public long getMessageSize() {
+ return mPduHeaders.getLongInteger(PduHeaders.MESSAGE_SIZE);
+ }
+
+ /**
+ * Set X-Mms-MessageSize value.
+ *
+ * @param value the value
+ */
+ @UnsupportedAppUsage
+ public void setMessageSize(long value) {
+ mPduHeaders.setLongInteger(value, PduHeaders.MESSAGE_SIZE);
+ }
+
+ /**
+ * Get X-Mms-Message-Class value.
+ * Message-class-value = Class-identifier | Token-text
+ * Class-identifier = Personal | Advertisement | Informational | Auto
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getMessageClass() {
+ return mPduHeaders.getTextString(PduHeaders.MESSAGE_CLASS);
+ }
+
+ /**
+ * Set X-Mms-Message-Class value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setMessageClass(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.MESSAGE_CLASS);
+ }
+
+ /**
+ * Get X-Mms-Read-Report value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public int getReadReport() {
+ return mPduHeaders.getOctet(PduHeaders.READ_REPORT);
+ }
+
+ /**
+ * Set X-Mms-Read-Report value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ */
+ @UnsupportedAppUsage
+ public void setReadReport(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.READ_REPORT);
+ }
+
+ /**
+ * Set "To" value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setTo(EncodedStringValue[] value) {
+ mPduHeaders.setEncodedStringValues(value, PduHeaders.TO);
+ }
+
+ /**
+ * Get X-Mms-Transaction-Id field value.
+ *
+ * @return the X-Mms-Report-Allowed value
+ */
+ @UnsupportedAppUsage
+ public byte[] getTransactionId() {
+ return mPduHeaders.getTextString(PduHeaders.TRANSACTION_ID);
+ }
+
+ /**
+ * Set X-Mms-Transaction-Id field value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setTransactionId(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.TRANSACTION_ID);
+ }
+
+ /*
+ * Optional, not supported header fields:
+ *
+ * public byte getAdaptationAllowed() {return 0};
+ * public void setAdaptationAllowed(btye value) {};
+ *
+ * public byte[] getApplicId() {return null;}
+ * public void setApplicId(byte[] value) {}
+ *
+ * public byte[] getAuxApplicId() {return null;}
+ * public void getAuxApplicId(byte[] value) {}
+ *
+ * public byte getContentClass() {return 0x00;}
+ * public void setApplicId(byte value) {}
+ *
+ * public long getDeliveryTime() {return 0};
+ * public void setDeliveryTime(long value) {};
+ *
+ * public byte getDrmContent() {return 0x00;}
+ * public void setDrmContent(byte value) {}
+ *
+ * public MmFlagsValue getMmFlags() {return null;}
+ * public void setMmFlags(MmFlagsValue value) {}
+ *
+ * public MmStateValue getMmState() {return null;}
+ * public void getMmState(MmStateValue value) {}
+ *
+ * public byte[] getReplyApplicId() {return 0x00;}
+ * public void setReplyApplicId(byte[] value) {}
+ *
+ * public byte getReplyCharging() {return 0x00;}
+ * public void setReplyCharging(byte value) {}
+ *
+ * public byte getReplyChargingDeadline() {return 0x00;}
+ * public void setReplyChargingDeadline(byte value) {}
+ *
+ * public byte[] getReplyChargingId() {return 0x00;}
+ * public void setReplyChargingId(byte[] value) {}
+ *
+ * public long getReplyChargingSize() {return 0;}
+ * public void setReplyChargingSize(long value) {}
+ *
+ * public byte[] getReplyApplicId() {return 0x00;}
+ * public void setReplyApplicId(byte[] value) {}
+ *
+ * public byte getStore() {return 0x00;}
+ * public void setStore(byte value) {}
+ */
+}
diff --git a/telephony/java/com/google/android/mms/pdu/package.html b/telephony/java/com/google/android/mms/pdu/package.html
new file mode 100755
index 000000000000..c9f96a66ab3b
--- /dev/null
+++ b/telephony/java/com/google/android/mms/pdu/package.html
@@ -0,0 +1,5 @@
+<body>
+
+{@hide}
+
+</body>
diff --git a/telephony/java/com/google/android/mms/util/AbstractCache.java b/telephony/java/com/google/android/mms/util/AbstractCache.java
new file mode 100644
index 000000000000..ab5d48a4ce3d
--- /dev/null
+++ b/telephony/java/com/google/android/mms/util/AbstractCache.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2008 Esmertec AG.
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.util;
+
+import android.util.Log;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import java.util.HashMap;
+
+public abstract class AbstractCache<K, V> {
+ private static final String TAG = "AbstractCache";
+ private static final boolean DEBUG = false;
+ private static final boolean LOCAL_LOGV = false;
+
+ private static final int MAX_CACHED_ITEMS = 500;
+
+ private final HashMap<K, CacheEntry<V>> mCacheMap;
+
+ @UnsupportedAppUsage
+ protected AbstractCache() {
+ mCacheMap = new HashMap<K, CacheEntry<V>>();
+ }
+
+ @UnsupportedAppUsage
+ public boolean put(K key, V value) {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "Trying to put " + key + " into cache.");
+ }
+
+ if (mCacheMap.size() >= MAX_CACHED_ITEMS) {
+ // TODO Should remove the oldest or least hit cached entry
+ // and then cache the new one.
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "Failed! size limitation reached.");
+ }
+ return false;
+ }
+
+ if (key != null) {
+ CacheEntry<V> cacheEntry = new CacheEntry<V>();
+ cacheEntry.value = value;
+ mCacheMap.put(key, cacheEntry);
+
+ if (LOCAL_LOGV) {
+ Log.v(TAG, key + " cached, " + mCacheMap.size() + " items total.");
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @UnsupportedAppUsage
+ public V get(K key) {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "Trying to get " + key + " from cache.");
+ }
+
+ if (key != null) {
+ CacheEntry<V> cacheEntry = mCacheMap.get(key);
+ if (cacheEntry != null) {
+ cacheEntry.hit++;
+ if (LOCAL_LOGV) {
+ Log.v(TAG, key + " hit " + cacheEntry.hit + " times.");
+ }
+ return cacheEntry.value;
+ }
+ }
+ return null;
+ }
+
+ @UnsupportedAppUsage
+ public V purge(K key) {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "Trying to purge " + key);
+ }
+
+ CacheEntry<V> v = mCacheMap.remove(key);
+
+ if (LOCAL_LOGV) {
+ Log.v(TAG, mCacheMap.size() + " items cached.");
+ }
+
+ return v != null ? v.value : null;
+ }
+
+ @UnsupportedAppUsage
+ public void purgeAll() {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "Purging cache, " + mCacheMap.size()
+ + " items dropped.");
+ }
+ mCacheMap.clear();
+ }
+
+ public int size() {
+ return mCacheMap.size();
+ }
+
+ private static class CacheEntry<V> {
+ int hit;
+ V value;
+ }
+}
diff --git a/telephony/java/com/google/android/mms/util/DownloadDrmHelper.java b/telephony/java/com/google/android/mms/util/DownloadDrmHelper.java
new file mode 100644
index 000000000000..118de465a518
--- /dev/null
+++ b/telephony/java/com/google/android/mms/util/DownloadDrmHelper.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.google.android.mms.util;
+
+import android.content.Context;
+import android.drm.DrmManagerClient;
+import android.util.Log;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+public class DownloadDrmHelper {
+ private static final String TAG = "DownloadDrmHelper";
+
+ /** The MIME type of special DRM files */
+ public static final String MIMETYPE_DRM_MESSAGE = "application/vnd.oma.drm.message";
+
+ /** The extensions of special DRM files */
+ public static final String EXTENSION_DRM_MESSAGE = ".dm";
+
+ public static final String EXTENSION_INTERNAL_FWDL = ".fl";
+
+ /**
+ * Checks if the Media Type is a DRM Media Type
+ *
+ * @param drmManagerClient A DrmManagerClient
+ * @param mimetype Media Type to check
+ * @return True if the Media Type is DRM else false
+ */
+ public static boolean isDrmMimeType(Context context, String mimetype) {
+ boolean result = false;
+ if (context != null) {
+ try {
+ DrmManagerClient drmClient = new DrmManagerClient(context);
+ if (drmClient != null && mimetype != null && mimetype.length() > 0) {
+ result = drmClient.canHandle("", mimetype);
+ }
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG,
+ "DrmManagerClient instance could not be created, context is Illegal.");
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "DrmManagerClient didn't initialize properly.");
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Checks if the Media Type needs to be DRM converted
+ *
+ * @param mimetype Media type of the content
+ * @return True if convert is needed else false
+ */
+ @UnsupportedAppUsage
+ public static boolean isDrmConvertNeeded(String mimetype) {
+ return MIMETYPE_DRM_MESSAGE.equals(mimetype);
+ }
+
+ /**
+ * Modifies the file extension for a DRM Forward Lock file NOTE: This
+ * function shouldn't be called if the file shouldn't be DRM converted
+ */
+ @UnsupportedAppUsage
+ public static String modifyDrmFwLockFileExtension(String filename) {
+ if (filename != null) {
+ int extensionIndex;
+ extensionIndex = filename.lastIndexOf(".");
+ if (extensionIndex != -1) {
+ filename = filename.substring(0, extensionIndex);
+ }
+ filename = filename.concat(EXTENSION_INTERNAL_FWDL);
+ }
+ return filename;
+ }
+
+ /**
+ * Gets the original mime type of DRM protected content.
+ *
+ * @param context The context
+ * @param path Path to the file
+ * @param containingMime The current mime type of the file i.e. the
+ * containing mime type
+ * @return The original mime type of the file if DRM protected else the
+ * currentMime
+ */
+ public static String getOriginalMimeType(Context context, String path, String containingMime) {
+ String result = containingMime;
+ DrmManagerClient drmClient = new DrmManagerClient(context);
+ try {
+ if (drmClient.canHandle(path, null)) {
+ result = drmClient.getOriginalMimeType(path);
+ }
+ } catch (IllegalArgumentException ex) {
+ Log.w(TAG,
+ "Can't get original mime type since path is null or empty string.");
+ } catch (IllegalStateException ex) {
+ Log.w(TAG, "DrmManagerClient didn't initialize properly.");
+ }
+ return result;
+ }
+}
diff --git a/telephony/java/com/google/android/mms/util/DrmConvertSession.java b/telephony/java/com/google/android/mms/util/DrmConvertSession.java
new file mode 100644
index 000000000000..0e8ec91f4ef6
--- /dev/null
+++ b/telephony/java/com/google/android/mms/util/DrmConvertSession.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.google.android.mms.util;
+
+import android.content.Context;
+import android.drm.DrmConvertedStatus;
+import android.drm.DrmManagerClient;
+import android.provider.Downloads;
+import android.util.Log;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+
+public class DrmConvertSession {
+ private DrmManagerClient mDrmClient;
+ private int mConvertSessionId;
+ private static final String TAG = "DrmConvertSession";
+
+ private DrmConvertSession(DrmManagerClient drmClient, int convertSessionId) {
+ mDrmClient = drmClient;
+ mConvertSessionId = convertSessionId;
+ }
+
+ /**
+ * Start of converting a file.
+ *
+ * @param context The context of the application running the convert session.
+ * @param mimeType Mimetype of content that shall be converted.
+ * @return A convert session or null in case an error occurs.
+ */
+ @UnsupportedAppUsage
+ public static DrmConvertSession open(Context context, String mimeType) {
+ DrmManagerClient drmClient = null;
+ int convertSessionId = -1;
+ if (context != null && mimeType != null && !mimeType.equals("")) {
+ try {
+ drmClient = new DrmManagerClient(context);
+ try {
+ convertSessionId = drmClient.openConvertSession(mimeType);
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG, "Conversion of Mimetype: " + mimeType
+ + " is not supported.", e);
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Could not access Open DrmFramework.", e);
+ }
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG,
+ "DrmManagerClient instance could not be created, context is Illegal.");
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "DrmManagerClient didn't initialize properly.");
+ }
+ }
+
+ if (drmClient == null || convertSessionId < 0) {
+ return null;
+ } else {
+ return new DrmConvertSession(drmClient, convertSessionId);
+ }
+ }
+ /**
+ * Convert a buffer of data to protected format.
+ *
+ * @param buffer Buffer filled with data to convert.
+ * @param size The number of bytes that shall be converted.
+ * @return A Buffer filled with converted data, if execution is ok, in all
+ * other case null.
+ */
+ @UnsupportedAppUsage
+ public byte [] convert(byte[] inBuffer, int size) {
+ byte[] result = null;
+ if (inBuffer != null) {
+ DrmConvertedStatus convertedStatus = null;
+ try {
+ if (size != inBuffer.length) {
+ byte[] buf = new byte[size];
+ System.arraycopy(inBuffer, 0, buf, 0, size);
+ convertedStatus = mDrmClient.convertData(mConvertSessionId, buf);
+ } else {
+ convertedStatus = mDrmClient.convertData(mConvertSessionId, inBuffer);
+ }
+
+ if (convertedStatus != null &&
+ convertedStatus.statusCode == DrmConvertedStatus.STATUS_OK &&
+ convertedStatus.convertedData != null) {
+ result = convertedStatus.convertedData;
+ }
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG, "Buffer with data to convert is illegal. Convertsession: "
+ + mConvertSessionId, e);
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Could not convert data. Convertsession: " +
+ mConvertSessionId, e);
+ }
+ } else {
+ throw new IllegalArgumentException("Parameter inBuffer is null");
+ }
+ return result;
+ }
+
+ /**
+ * Ends a conversion session of a file.
+ *
+ * @param fileName The filename of the converted file.
+ * @return Downloads.Impl.STATUS_SUCCESS if execution is ok.
+ * Downloads.Impl.STATUS_FILE_ERROR in case converted file can not
+ * be accessed. Downloads.Impl.STATUS_NOT_ACCEPTABLE if a problem
+ * occurs when accessing drm framework.
+ * Downloads.Impl.STATUS_UNKNOWN_ERROR if a general error occurred.
+ */
+ @UnsupportedAppUsage
+ public int close(String filename) {
+ DrmConvertedStatus convertedStatus = null;
+ int result = Downloads.Impl.STATUS_UNKNOWN_ERROR;
+ if (mDrmClient != null && mConvertSessionId >= 0) {
+ try {
+ convertedStatus = mDrmClient.closeConvertSession(mConvertSessionId);
+ if (convertedStatus == null ||
+ convertedStatus.statusCode != DrmConvertedStatus.STATUS_OK ||
+ convertedStatus.convertedData == null) {
+ result = Downloads.Impl.STATUS_NOT_ACCEPTABLE;
+ } else {
+ RandomAccessFile rndAccessFile = null;
+ try {
+ rndAccessFile = new RandomAccessFile(filename, "rw");
+ rndAccessFile.seek(convertedStatus.offset);
+ rndAccessFile.write(convertedStatus.convertedData);
+ result = Downloads.Impl.STATUS_SUCCESS;
+ } catch (FileNotFoundException e) {
+ result = Downloads.Impl.STATUS_FILE_ERROR;
+ Log.w(TAG, "File: " + filename + " could not be found.", e);
+ } catch (IOException e) {
+ result = Downloads.Impl.STATUS_FILE_ERROR;
+ Log.w(TAG, "Could not access File: " + filename + " .", e);
+ } catch (IllegalArgumentException e) {
+ result = Downloads.Impl.STATUS_FILE_ERROR;
+ Log.w(TAG, "Could not open file in mode: rw", e);
+ } catch (SecurityException e) {
+ Log.w(TAG, "Access to File: " + filename +
+ " was denied denied by SecurityManager.", e);
+ } finally {
+ if (rndAccessFile != null) {
+ try {
+ rndAccessFile.close();
+ } catch (IOException e) {
+ result = Downloads.Impl.STATUS_FILE_ERROR;
+ Log.w(TAG, "Failed to close File:" + filename
+ + ".", e);
+ }
+ }
+ }
+ }
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Could not close convertsession. Convertsession: " +
+ mConvertSessionId, e);
+ }
+ }
+ return result;
+ }
+}
diff --git a/telephony/java/com/google/android/mms/util/PduCache.java b/telephony/java/com/google/android/mms/util/PduCache.java
new file mode 100644
index 000000000000..94e38946f632
--- /dev/null
+++ b/telephony/java/com/google/android/mms/util/PduCache.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2008 Esmertec AG.
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.util;
+
+import android.content.ContentUris;
+import android.content.UriMatcher;
+import android.net.Uri;
+import android.provider.Telephony.Mms;
+import android.util.Log;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import java.util.HashMap;
+import java.util.HashSet;
+
+public final class PduCache extends AbstractCache<Uri, PduCacheEntry> {
+ private static final String TAG = "PduCache";
+ private static final boolean DEBUG = false;
+ private static final boolean LOCAL_LOGV = false;
+
+ private static final int MMS_ALL = 0;
+ private static final int MMS_ALL_ID = 1;
+ private static final int MMS_INBOX = 2;
+ private static final int MMS_INBOX_ID = 3;
+ private static final int MMS_SENT = 4;
+ private static final int MMS_SENT_ID = 5;
+ private static final int MMS_DRAFTS = 6;
+ private static final int MMS_DRAFTS_ID = 7;
+ private static final int MMS_OUTBOX = 8;
+ private static final int MMS_OUTBOX_ID = 9;
+ private static final int MMS_CONVERSATION = 10;
+ private static final int MMS_CONVERSATION_ID = 11;
+
+ private static final UriMatcher URI_MATCHER;
+ private static final HashMap<Integer, Integer> MATCH_TO_MSGBOX_ID_MAP;
+
+ private static PduCache sInstance;
+
+ static {
+ URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
+ URI_MATCHER.addURI("mms", null, MMS_ALL);
+ URI_MATCHER.addURI("mms", "#", MMS_ALL_ID);
+ URI_MATCHER.addURI("mms", "inbox", MMS_INBOX);
+ URI_MATCHER.addURI("mms", "inbox/#", MMS_INBOX_ID);
+ URI_MATCHER.addURI("mms", "sent", MMS_SENT);
+ URI_MATCHER.addURI("mms", "sent/#", MMS_SENT_ID);
+ URI_MATCHER.addURI("mms", "drafts", MMS_DRAFTS);
+ URI_MATCHER.addURI("mms", "drafts/#", MMS_DRAFTS_ID);
+ URI_MATCHER.addURI("mms", "outbox", MMS_OUTBOX);
+ URI_MATCHER.addURI("mms", "outbox/#", MMS_OUTBOX_ID);
+ URI_MATCHER.addURI("mms-sms", "conversations", MMS_CONVERSATION);
+ URI_MATCHER.addURI("mms-sms", "conversations/#", MMS_CONVERSATION_ID);
+
+ MATCH_TO_MSGBOX_ID_MAP = new HashMap<Integer, Integer>();
+ MATCH_TO_MSGBOX_ID_MAP.put(MMS_INBOX, Mms.MESSAGE_BOX_INBOX);
+ MATCH_TO_MSGBOX_ID_MAP.put(MMS_SENT, Mms.MESSAGE_BOX_SENT);
+ MATCH_TO_MSGBOX_ID_MAP.put(MMS_DRAFTS, Mms.MESSAGE_BOX_DRAFTS);
+ MATCH_TO_MSGBOX_ID_MAP.put(MMS_OUTBOX, Mms.MESSAGE_BOX_OUTBOX);
+ }
+
+ private final HashMap<Integer, HashSet<Uri>> mMessageBoxes;
+ private final HashMap<Long, HashSet<Uri>> mThreads;
+ private final HashSet<Uri> mUpdating;
+
+ @UnsupportedAppUsage
+ private PduCache() {
+ mMessageBoxes = new HashMap<Integer, HashSet<Uri>>();
+ mThreads = new HashMap<Long, HashSet<Uri>>();
+ mUpdating = new HashSet<Uri>();
+ }
+
+ @UnsupportedAppUsage
+ synchronized public static final PduCache getInstance() {
+ if (sInstance == null) {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "Constructing new PduCache instance.");
+ }
+ sInstance = new PduCache();
+ }
+ return sInstance;
+ }
+
+ @Override
+ synchronized public boolean put(Uri uri, PduCacheEntry entry) {
+ int msgBoxId = entry.getMessageBox();
+ HashSet<Uri> msgBox = mMessageBoxes.get(msgBoxId);
+ if (msgBox == null) {
+ msgBox = new HashSet<Uri>();
+ mMessageBoxes.put(msgBoxId, msgBox);
+ }
+
+ long threadId = entry.getThreadId();
+ HashSet<Uri> thread = mThreads.get(threadId);
+ if (thread == null) {
+ thread = new HashSet<Uri>();
+ mThreads.put(threadId, thread);
+ }
+
+ Uri finalKey = normalizeKey(uri);
+ boolean result = super.put(finalKey, entry);
+ if (result) {
+ msgBox.add(finalKey);
+ thread.add(finalKey);
+ }
+ setUpdating(uri, false);
+ return result;
+ }
+
+ synchronized public void setUpdating(Uri uri, boolean updating) {
+ if (updating) {
+ mUpdating.add(uri);
+ } else {
+ mUpdating.remove(uri);
+ }
+ }
+
+ @UnsupportedAppUsage
+ synchronized public boolean isUpdating(Uri uri) {
+ return mUpdating.contains(uri);
+ }
+
+ @Override
+ @UnsupportedAppUsage
+ synchronized public PduCacheEntry purge(Uri uri) {
+ int match = URI_MATCHER.match(uri);
+ switch (match) {
+ case MMS_ALL_ID:
+ return purgeSingleEntry(uri);
+ case MMS_INBOX_ID:
+ case MMS_SENT_ID:
+ case MMS_DRAFTS_ID:
+ case MMS_OUTBOX_ID:
+ String msgId = uri.getLastPathSegment();
+ return purgeSingleEntry(Uri.withAppendedPath(Mms.CONTENT_URI, msgId));
+ // Implicit batch of purge, return null.
+ case MMS_ALL:
+ case MMS_CONVERSATION:
+ purgeAll();
+ return null;
+ case MMS_INBOX:
+ case MMS_SENT:
+ case MMS_DRAFTS:
+ case MMS_OUTBOX:
+ purgeByMessageBox(MATCH_TO_MSGBOX_ID_MAP.get(match));
+ return null;
+ case MMS_CONVERSATION_ID:
+ purgeByThreadId(ContentUris.parseId(uri));
+ return null;
+ default:
+ return null;
+ }
+ }
+
+ private PduCacheEntry purgeSingleEntry(Uri key) {
+ mUpdating.remove(key);
+ PduCacheEntry entry = super.purge(key);
+ if (entry != null) {
+ removeFromThreads(key, entry);
+ removeFromMessageBoxes(key, entry);
+ return entry;
+ }
+ return null;
+ }
+
+ @UnsupportedAppUsage
+ @Override
+ synchronized public void purgeAll() {
+ super.purgeAll();
+
+ mMessageBoxes.clear();
+ mThreads.clear();
+ mUpdating.clear();
+ }
+
+ /**
+ * @param uri The Uri to be normalized.
+ * @return Uri The normalized key of cached entry.
+ */
+ private Uri normalizeKey(Uri uri) {
+ int match = URI_MATCHER.match(uri);
+ Uri normalizedKey = null;
+
+ switch (match) {
+ case MMS_ALL_ID:
+ normalizedKey = uri;
+ break;
+ case MMS_INBOX_ID:
+ case MMS_SENT_ID:
+ case MMS_DRAFTS_ID:
+ case MMS_OUTBOX_ID:
+ String msgId = uri.getLastPathSegment();
+ normalizedKey = Uri.withAppendedPath(Mms.CONTENT_URI, msgId);
+ break;
+ default:
+ return null;
+ }
+
+ if (LOCAL_LOGV) {
+ Log.v(TAG, uri + " -> " + normalizedKey);
+ }
+ return normalizedKey;
+ }
+
+ private void purgeByMessageBox(Integer msgBoxId) {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "Purge cache in message box: " + msgBoxId);
+ }
+
+ if (msgBoxId != null) {
+ HashSet<Uri> msgBox = mMessageBoxes.remove(msgBoxId);
+ if (msgBox != null) {
+ for (Uri key : msgBox) {
+ mUpdating.remove(key);
+ PduCacheEntry entry = super.purge(key);
+ if (entry != null) {
+ removeFromThreads(key, entry);
+ }
+ }
+ }
+ }
+ }
+
+ private void removeFromThreads(Uri key, PduCacheEntry entry) {
+ HashSet<Uri> thread = mThreads.get(entry.getThreadId());
+ if (thread != null) {
+ thread.remove(key);
+ }
+ }
+
+ private void purgeByThreadId(long threadId) {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "Purge cache in thread: " + threadId);
+ }
+
+ HashSet<Uri> thread = mThreads.remove(threadId);
+ if (thread != null) {
+ for (Uri key : thread) {
+ mUpdating.remove(key);
+ PduCacheEntry entry = super.purge(key);
+ if (entry != null) {
+ removeFromMessageBoxes(key, entry);
+ }
+ }
+ }
+ }
+
+ private void removeFromMessageBoxes(Uri key, PduCacheEntry entry) {
+ HashSet<Uri> msgBox = mThreads.get(Long.valueOf(entry.getMessageBox()));
+ if (msgBox != null) {
+ msgBox.remove(key);
+ }
+ }
+}
diff --git a/telephony/java/com/google/android/mms/util/PduCacheEntry.java b/telephony/java/com/google/android/mms/util/PduCacheEntry.java
new file mode 100644
index 000000000000..1ecd1bf93e7f
--- /dev/null
+++ b/telephony/java/com/google/android/mms/util/PduCacheEntry.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2008 Esmertec AG.
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.util;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import com.google.android.mms.pdu.GenericPdu;
+
+public final class PduCacheEntry {
+ private final GenericPdu mPdu;
+ private final int mMessageBox;
+ private final long mThreadId;
+
+ @UnsupportedAppUsage
+ public PduCacheEntry(GenericPdu pdu, int msgBox, long threadId) {
+ mPdu = pdu;
+ mMessageBox = msgBox;
+ mThreadId = threadId;
+ }
+
+ @UnsupportedAppUsage
+ public GenericPdu getPdu() {
+ return mPdu;
+ }
+
+ @UnsupportedAppUsage
+ public int getMessageBox() {
+ return mMessageBox;
+ }
+
+ @UnsupportedAppUsage
+ public long getThreadId() {
+ return mThreadId;
+ }
+}
diff --git a/telephony/java/com/google/android/mms/util/SqliteWrapper.java b/telephony/java/com/google/android/mms/util/SqliteWrapper.java
new file mode 100644
index 000000000000..2dd1dc11c2a9
--- /dev/null
+++ b/telephony/java/com/google/android/mms/util/SqliteWrapper.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2008 Esmertec AG.
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.util;
+
+import android.app.ActivityManager;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteException;
+import android.net.Uri;
+import android.util.Log;
+import android.widget.Toast;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+public final class SqliteWrapper {
+ private static final String TAG = "SqliteWrapper";
+ private static final String SQLITE_EXCEPTION_DETAIL_MESSAGE
+ = "unable to open database file";
+
+ private SqliteWrapper() {
+ // Forbidden being instantiated.
+ }
+
+ // FIXME: It looks like outInfo.lowMemory does not work well as we expected.
+ // after run command: adb shell fillup -p 100, outInfo.lowMemory is still false.
+ private static boolean isLowMemory(Context context) {
+ if (null == context) {
+ return false;
+ }
+
+ ActivityManager am = (ActivityManager)
+ context.getSystemService(Context.ACTIVITY_SERVICE);
+ ActivityManager.MemoryInfo outInfo = new ActivityManager.MemoryInfo();
+ am.getMemoryInfo(outInfo);
+
+ return outInfo.lowMemory;
+ }
+
+ // FIXME: need to optimize this method.
+ private static boolean isLowMemory(SQLiteException e) {
+ return e.getMessage().equals(SQLITE_EXCEPTION_DETAIL_MESSAGE);
+ }
+
+ @UnsupportedAppUsage
+ public static void checkSQLiteException(Context context, SQLiteException e) {
+ if (isLowMemory(e)) {
+ Toast.makeText(context, com.android.internal.R.string.low_memory,
+ Toast.LENGTH_SHORT).show();
+ } else {
+ throw e;
+ }
+ }
+
+ @UnsupportedAppUsage
+ public static Cursor query(Context context, ContentResolver resolver, Uri uri,
+ String[] projection, String selection, String[] selectionArgs, String sortOrder) {
+ try {
+ return resolver.query(uri, projection, selection, selectionArgs, sortOrder);
+ } catch (SQLiteException e) {
+ Log.e(TAG, "Catch a SQLiteException when query: ", e);
+ checkSQLiteException(context, e);
+ return null;
+ }
+ }
+
+ @UnsupportedAppUsage
+ public static boolean requery(Context context, Cursor cursor) {
+ try {
+ return cursor.requery();
+ } catch (SQLiteException e) {
+ Log.e(TAG, "Catch a SQLiteException when requery: ", e);
+ checkSQLiteException(context, e);
+ return false;
+ }
+ }
+ @UnsupportedAppUsage
+ public static int update(Context context, ContentResolver resolver, Uri uri,
+ ContentValues values, String where, String[] selectionArgs) {
+ try {
+ return resolver.update(uri, values, where, selectionArgs);
+ } catch (SQLiteException e) {
+ Log.e(TAG, "Catch a SQLiteException when update: ", e);
+ checkSQLiteException(context, e);
+ return -1;
+ }
+ }
+
+ @UnsupportedAppUsage
+ public static int delete(Context context, ContentResolver resolver, Uri uri,
+ String where, String[] selectionArgs) {
+ try {
+ return resolver.delete(uri, where, selectionArgs);
+ } catch (SQLiteException e) {
+ Log.e(TAG, "Catch a SQLiteException when delete: ", e);
+ checkSQLiteException(context, e);
+ return -1;
+ }
+ }
+
+ @UnsupportedAppUsage
+ public static Uri insert(Context context, ContentResolver resolver,
+ Uri uri, ContentValues values) {
+ try {
+ return resolver.insert(uri, values);
+ } catch (SQLiteException e) {
+ Log.e(TAG, "Catch a SQLiteException when insert: ", e);
+ checkSQLiteException(context, e);
+ return null;
+ }
+ }
+}
diff --git a/telephony/java/com/google/android/mms/util/package.html b/telephony/java/com/google/android/mms/util/package.html
new file mode 100755
index 000000000000..c9f96a66ab3b
--- /dev/null
+++ b/telephony/java/com/google/android/mms/util/package.html
@@ -0,0 +1,5 @@
+<body>
+
+{@hide}
+
+</body>
diff --git a/test-mock/Android.bp b/test-mock/Android.bp
index 34ac3dcc824f..adc9e2251c0b 100644
--- a/test-mock/Android.bp
+++ b/test-mock/Android.bp
@@ -21,7 +21,7 @@ java_sdk_library {
srcs: [
"src/**/*.java",
- ":framework-srcs",
+ ":framework-all-sources",
],
api_packages: [
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/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
index 9a6033058c53..79f5095010e8 100644
--- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
@@ -702,6 +702,141 @@ public class PackageWatchdogTest {
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) {
InstrumentationRegistry
.getInstrumentation()
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index 7ffa5ffc09fe..137fbd671865 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -246,6 +246,36 @@ class ValueBodyPrinter : public ConstValueVisitor {
Printer* printer_;
};
+std::string OverlayablePoliciesToString(OverlayableItem::PolicyFlags policies) {
+ static const std::map<OverlayableItem::PolicyFlags, std::string> kFlagToString = {
+ {OverlayableItem::kPublic, "public"},
+ {OverlayableItem::kSystem, "system"},
+ {OverlayableItem::kVendor, "vendor"},
+ {OverlayableItem::kProduct, "product"},
+ {OverlayableItem::kSignature, "signature"},
+ {OverlayableItem::kOdm, "odm"},
+ {OverlayableItem::kOem, "oem"},
+ };
+ std::string str;
+ for (auto const& policy : kFlagToString) {
+ if ((policies & policy.first) != policy.first) {
+ continue;
+ }
+ if (!str.empty()) {
+ str.append("|");
+ }
+ str.append(policy.second);
+ policies &= ~policy.first;
+ }
+ if (policies != 0) {
+ if (!str.empty()) {
+ str.append("|");
+ }
+ str.append(StringPrintf("0x%08x", policies));
+ }
+ return !str.empty() ? str : "none";
+}
+
} // namespace
void Debug::PrintTable(const ResourceTable& table, const DebugPrintTableOptions& options,
@@ -312,6 +342,10 @@ void Debug::PrintTable(const ResourceTable& table, const DebugPrintTableOptions&
break;
}
+ if (entry->overlayable_item) {
+ printer->Print(" OVERLAYABLE");
+ }
+
printer->Println();
if (options.show_values) {
@@ -525,4 +559,62 @@ void Debug::DumpXml(const xml::XmlResource& doc, Printer* printer) {
doc.root->Accept(&xml_visitor);
}
+struct DumpOverlayableEntry {
+ std::string overlayable_section;
+ std::string policy_subsection;
+ std::string resource_name;
+};
+
+void Debug::DumpOverlayable(const ResourceTable& table, text::Printer* printer) {
+ std::vector<DumpOverlayableEntry> items;
+ for (const auto& package : table.packages) {
+ for (const auto& type : package->types) {
+ for (const auto& entry : type->entries) {
+ if (entry->overlayable_item) {
+ const auto& overlayable_item = entry->overlayable_item.value();
+ const auto overlayable_section = StringPrintf(R"(name="%s" actor="%s")",
+ overlayable_item.overlayable->name.c_str(),
+ overlayable_item.overlayable->actor.c_str());
+ const auto policy_subsection = StringPrintf(R"(policies="%s")",
+ OverlayablePoliciesToString(overlayable_item.policies).c_str());
+ const auto value =
+ StringPrintf("%s/%s", to_string(type->type).data(), entry->name.c_str());
+ items.push_back(DumpOverlayableEntry{overlayable_section, policy_subsection, value});
+ }
+ }
+ }
+ }
+
+ std::sort(items.begin(), items.end(),
+ [](const DumpOverlayableEntry& a, const DumpOverlayableEntry& b) {
+ if (a.overlayable_section != b.overlayable_section) {
+ return a.overlayable_section < b.overlayable_section;
+ }
+ if (a.policy_subsection != b.policy_subsection) {
+ return a.policy_subsection < b.policy_subsection;
+ }
+ return a.resource_name < b.resource_name;
+ });
+
+ std::string last_overlayable_section;
+ std::string last_policy_subsection;
+ for (const auto& item : items) {
+ if (last_overlayable_section != item.overlayable_section) {
+ printer->Println(item.overlayable_section);
+ last_overlayable_section = item.overlayable_section;
+ }
+ if (last_policy_subsection != item.policy_subsection) {
+ printer->Indent();
+ printer->Println(item.policy_subsection);
+ last_policy_subsection = item.policy_subsection;
+ printer->Undent();
+ }
+ printer->Indent();
+ printer->Indent();
+ printer->Println(item.resource_name);
+ printer->Undent();
+ printer->Undent();
+ }
+}
+
} // namespace aapt
diff --git a/tools/aapt2/Debug.h b/tools/aapt2/Debug.h
index a43197cacf7b..9443d606d7e5 100644
--- a/tools/aapt2/Debug.h
+++ b/tools/aapt2/Debug.h
@@ -39,6 +39,7 @@ struct Debug {
static void DumpHex(const void* data, size_t len);
static void DumpXml(const xml::XmlResource& doc, text::Printer* printer);
static void DumpResStringPool(const android::ResStringPool* pool, text::Printer* printer);
+ static void DumpOverlayable(const ResourceTable& table, text::Printer* printer);
};
} // namespace aapt
diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp
index 213bdd2372ec..7966ba27ebd8 100644
--- a/tools/aapt2/Main.cpp
+++ b/tools/aapt2/Main.cpp
@@ -177,6 +177,11 @@ int MainImpl(int argc, char** argv) {
return main_command->Execute(args, &std::cerr);
}
+// TODO(b/141312058) stop leaks
+extern "C" const char *__asan_default_options() {
+ return "detect_leaks=0";
+}
+
int main(int argc, char** argv) {
#ifdef _WIN32
LPWSTR* wide_argv = CommandLineToArgvW(GetCommandLineW(), &argc);
diff --git a/tools/aapt2/cmd/Dump.cpp b/tools/aapt2/cmd/Dump.cpp
index 429aff1ff594..3982d12f6036 100644
--- a/tools/aapt2/cmd/Dump.cpp
+++ b/tools/aapt2/cmd/Dump.cpp
@@ -394,6 +394,17 @@ int DumpXmlTreeCommand::Dump(LoadedApk* apk) {
return 0;
}
+int DumpOverlayableCommand::Dump(LoadedApk* apk) {
+ ResourceTable* table = apk->GetResourceTable();
+ if (!table) {
+ GetDiagnostics()->Error(DiagMessage() << "Failed to retrieve resource table");
+ return 1;
+ }
+
+ Debug::DumpOverlayable(*table, GetPrinter());
+ return 0;
+}
+
const char DumpBadgerCommand::kBadgerData[2925] = {
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
diff --git a/tools/aapt2/cmd/Dump.h b/tools/aapt2/cmd/Dump.h
index 7ded9bcf8470..cd51f7a7718c 100644
--- a/tools/aapt2/cmd/Dump.h
+++ b/tools/aapt2/cmd/Dump.h
@@ -240,6 +240,16 @@ class DumpXmlTreeCommand : public DumpApkCommand {
std::vector<std::string> files_;
};
+class DumpOverlayableCommand : public DumpApkCommand {
+ public:
+ explicit DumpOverlayableCommand(text::Printer* printer, IDiagnostics* diag)
+ : DumpApkCommand("overlayable", printer, diag) {
+ SetDescription("Print the <overlayable> resources of an APK.");
+ }
+
+ int Dump(LoadedApk* apk) override;
+};
+
/** The default dump command. Performs no action because a subcommand is required. */
class DumpCommand : public Command {
public:
@@ -255,8 +265,8 @@ class DumpCommand : public Command {
AddOptionalSubcommand(util::make_unique<DumpTableCommand>(printer, diag_));
AddOptionalSubcommand(util::make_unique<DumpXmlStringsCommand>(printer, diag_));
AddOptionalSubcommand(util::make_unique<DumpXmlTreeCommand>(printer, diag_));
+ AddOptionalSubcommand(util::make_unique<DumpOverlayableCommand>(printer, diag_));
AddOptionalSubcommand(util::make_unique<DumpBadgerCommand>(printer), /* hidden */ true);
- // TODO(b/120609160): Add aapt2 overlayable dump command
}
int Action(const std::vector<std::string>& args) override {
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/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/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 66dc992d6dc7..075531ce158e 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -750,7 +750,10 @@ public class WifiScanner {
/**
* Enable/Disable wifi scanning.
- *
+ * Note: WifiService calls this after any client interface mode changes (i.e. a new interface
+ * set up or an existing interface torn down)
+ * If there are >= 1 active client interface, invoke setScanningEnabled(true)
+ * If there are 0 active client interface, invoke setScanningEnabled(false)
* {@hide}
*/
@RequiresPermission(Manifest.permission.NETWORK_STACK)